diff options
237 files changed, 19447 insertions, 6674 deletions
@@ -41,10 +41,15 @@ LOCAL_SRC_FILES:= \ src/core/SkRegion.cpp \ src/core/SkString.cpp \ src/core/SkUtils.cpp \ + src/core/SkFlate.cpp \ + src/core/SkMallocPixelRef.cpp \ + src/core/SkRegion_rects.cpp \ + src/core/SkScalar.cpp \ src/ports/SkDebug_android.cpp \ src/effects/Sk1DPathEffect.cpp \ src/effects/Sk2DPathEffect.cpp \ src/effects/SkAvoidXfermode.cpp \ + src/effects/SkBitmapCache.cpp \ src/effects/SkBlurDrawLooper.cpp \ src/effects/SkBlurMask.cpp \ src/effects/SkBlurMaskFilter.cpp \ @@ -150,6 +155,7 @@ LOCAL_SRC_FILES:= \ src/core/SkQuadClipper.cpp \ src/core/SkRasterizer.cpp \ src/core/SkRefCnt.cpp \ + src/core/SkRefDict.cpp \ src/core/SkRegion_path.cpp \ src/core/SkScalerContext.cpp \ src/core/SkScan.cpp \ @@ -194,7 +200,7 @@ LOCAL_SRC_FILES += \ LOCAL_SHARED_LIBRARIES := \ libcutils \ - libemoji \ + libemoji \ libjpeg \ libutils \ libz @@ -218,7 +224,7 @@ LOCAL_C_INCLUDES += \ external/giflib \ external/jpeg \ external/webp/include \ - frameworks/opt/emoji + frameworks/opt/emoji ifeq ($(NO_FALLBACK_FONT),true) LOCAL_CFLAGS += -DNO_FALLBACK_FONT @@ -231,49 +237,6 @@ LOCAL_MODULE:= libskia include $(BUILD_SHARED_LIBRARY) ############################################################# -# Build the skia-opengl glue library -# - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm - -ifneq ($(ARCH_ARM_HAVE_VFP),true) - LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT -endif - -ifeq ($(ARCH_ARM_HAVE_NEON),true) - LOCAL_CFLAGS += -D__ARM_HAVE_NEON -endif - -LOCAL_SRC_FILES:= \ - src/gl/SkGL.cpp \ - src/gl/SkGLCanvas.cpp \ - src/gl/SkGLDevice.cpp \ - src/gl/SkGLDevice_SWLayer.cpp \ - src/gl/SkGLTextCache.cpp \ - src/gl/SkTextureCache.cpp - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libutils \ - libskia \ - libGLESv1_CM - -LOCAL_C_INCLUDES += \ - $(LOCAL_PATH)/src/core \ - $(LOCAL_PATH)/src/gl \ - $(LOCAL_PATH)/include/core \ - $(LOCAL_PATH)/include/effects \ - $(LOCAL_PATH)/include/utils - -LOCAL_LDLIBS += -lpthread - -LOCAL_MODULE:= libskiagl - -include $(BUILD_SHARED_LIBRARY) - -############################################################# # Build the skia tools # @@ -281,7 +244,7 @@ include $(BUILD_SHARED_LIBRARY) include $(BASE_PATH)/bench/Android.mk # golden-master (fidelity / regression test) -include $(BASE_PATH)/gm/Android.mk +#include $(BASE_PATH)/gm/Android.mk # unit-tests include $(BASE_PATH)/tests/Android.mk diff --git a/bench/Android.mk b/bench/Android.mk index 03c3e79..b20aef9 100644 --- a/bench/Android.mk +++ b/bench/Android.mk @@ -4,8 +4,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ BitmapBench.cpp \ - DecodeBench.cpp \ - FPSBench.cpp \ + DecodeBench.cpp \ + FPSBench.cpp \ RectBench.cpp \ RepeatTileBench.cpp \ TextBench.cpp \ @@ -23,7 +23,9 @@ LOCAL_C_INCLUDES := \ external/skia/include/core \ external/skia/include/images \ external/skia/include/utils \ - external/skia/include/effects + external/skia/include/effects \ + external/skia/gpu/include \ + external/skia/include/gpu #LOCAL_CFLAGS := diff --git a/bench/BenchTool/BenchTool.xcodeproj/project.pbxproj b/bench/BenchTool/BenchTool.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b637494 --- /dev/null +++ b/bench/BenchTool/BenchTool.xcodeproj/project.pbxproj @@ -0,0 +1,324 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 272FB43A0F11A19C00CA935D /* RectBench.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 272FB4390F11A19C00CA935D /* RectBench.cpp */; }; + 272FB4F30F11B40300CA935D /* SkBenchmark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 272FB4F20F11B40300CA935D /* SkBenchmark.cpp */; }; + 2752A08A0F14CE1300BBDC03 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2752A0890F14CE1300BBDC03 /* main.cpp */; }; + 27739F4D0F11439200F233EA /* libmaccore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F240F11404A00F233EA /* libmaccore.a */; }; + 27739F4E0F11439300F233EA /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F1A0F11403B00F233EA /* libcore.a */; }; + 27739F520F1143C000F233EA /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F510F1143C000F233EA /* Carbon.framework */; }; + 8DD76F6A0486A84900D96B5E /* BenchTool.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859E8B029090EE04C91782 /* BenchTool.1 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 27739F190F11403B00F233EA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 27739F120F11403B00F233EA /* core.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D2AAC046055464E500DB518D; + remoteInfo = core; + }; + 27739F230F11404A00F233EA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D2AAC046055464E500DB518D; + remoteInfo = maccore; + }; + 27739F3C0F11424800F233EA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = D2AAC045055464E500DB518D; + remoteInfo = maccore; + }; + 27739F3E0F11424C00F233EA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 27739F120F11403B00F233EA /* core.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = D2AAC045055464E500DB518D; + remoteInfo = core; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8DD76F690486A84900D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 8DD76F6A0486A84900D96B5E /* BenchTool.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 272FB4390F11A19C00CA935D /* RectBench.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RectBench.cpp; path = ../RectBench.cpp; sourceTree = SOURCE_ROOT; }; + 272FB4F20F11B40300CA935D /* SkBenchmark.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBenchmark.cpp; path = ../SkBenchmark.cpp; sourceTree = SOURCE_ROOT; }; + 2752A0890F14CE1300BBDC03 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = ../main.cpp; sourceTree = SOURCE_ROOT; }; + 27739F120F11403B00F233EA /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = core.xcodeproj; path = ../../xcode/core/core.xcodeproj; sourceTree = SOURCE_ROOT; }; + 27739F1C0F11404A00F233EA /* maccore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = maccore.xcodeproj; path = ../../xcode/maccore/maccore.xcodeproj; sourceTree = SOURCE_ROOT; }; + 27739F510F1143C000F233EA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; }; + 8DD76F6C0486A84900D96B5E /* BenchTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BenchTool; sourceTree = BUILT_PRODUCTS_DIR; }; + C6859E8B029090EE04C91782 /* BenchTool.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = BenchTool.1; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DD76F660486A84900D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 27739F4D0F11439200F233EA /* libmaccore.a in Frameworks */, + 27739F4E0F11439300F233EA /* libcore.a in Frameworks */, + 27739F520F1143C000F233EA /* Carbon.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* BenchTool */ = { + isa = PBXGroup; + children = ( + 27739F510F1143C000F233EA /* Carbon.framework */, + 27739F1C0F11404A00F233EA /* maccore.xcodeproj */, + 27739F120F11403B00F233EA /* core.xcodeproj */, + 08FB7795FE84155DC02AAC07 /* Source */, + C6859E8C029090F304C91782 /* Documentation */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = BenchTool; + sourceTree = "<group>"; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 2752A0890F14CE1300BBDC03 /* main.cpp */, + 272FB4F20F11B40300CA935D /* SkBenchmark.cpp */, + 272FB4390F11A19C00CA935D /* RectBench.cpp */, + ); + name = Source; + sourceTree = "<group>"; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76F6C0486A84900D96B5E /* BenchTool */, + ); + name = Products; + sourceTree = "<group>"; + }; + 27739F130F11403B00F233EA /* Products */ = { + isa = PBXGroup; + children = ( + 27739F1A0F11403B00F233EA /* libcore.a */, + ); + name = Products; + sourceTree = "<group>"; + }; + 27739F1D0F11404A00F233EA /* Products */ = { + isa = PBXGroup; + children = ( + 27739F240F11404A00F233EA /* libmaccore.a */, + ); + name = Products; + sourceTree = "<group>"; + }; + C6859E8C029090F304C91782 /* Documentation */ = { + isa = PBXGroup; + children = ( + C6859E8B029090EE04C91782 /* BenchTool.1 */, + ); + name = Documentation; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DD76F620486A84900D96B5E /* BenchTool */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "BenchTool" */; + buildPhases = ( + 8DD76F640486A84900D96B5E /* Sources */, + 8DD76F660486A84900D96B5E /* Frameworks */, + 8DD76F690486A84900D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 27739F3D0F11424800F233EA /* PBXTargetDependency */, + 27739F3F0F11424C00F233EA /* PBXTargetDependency */, + ); + name = BenchTool; + productInstallPath = "$(HOME)/bin"; + productName = BenchTool; + productReference = 8DD76F6C0486A84900D96B5E /* BenchTool */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "BenchTool" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* BenchTool */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 27739F130F11403B00F233EA /* Products */; + ProjectRef = 27739F120F11403B00F233EA /* core.xcodeproj */; + }, + { + ProductGroup = 27739F1D0F11404A00F233EA /* Products */; + ProjectRef = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 8DD76F620486A84900D96B5E /* BenchTool */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 27739F1A0F11403B00F233EA /* libcore.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libcore.a; + remoteRef = 27739F190F11403B00F233EA /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 27739F240F11404A00F233EA /* libmaccore.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libmaccore.a; + remoteRef = 27739F230F11404A00F233EA /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DD76F640486A84900D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 272FB43A0F11A19C00CA935D /* RectBench.cpp in Sources */, + 272FB4F30F11B40300CA935D /* SkBenchmark.cpp in Sources */, + 2752A08A0F14CE1300BBDC03 /* main.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 27739F3D0F11424800F233EA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = maccore; + targetProxy = 27739F3C0F11424800F233EA /* PBXContainerItemProxy */; + }; + 27739F3F0F11424C00F233EA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = core; + targetProxy = 27739F3E0F11424C00F233EA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1DEB923208733DC60010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "_GLIBCXX_DEBUG=1", + "_GLIBCXX_DEBUG_PEDANTIC=1", + ); + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = BenchTool; + }; + name = Debug; + }; + 1DEB923308733DC60010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = BenchTool; + }; + name = Release; + }; + 1DEB923608733DC60010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_THREADSAFE_STATICS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + USER_HEADER_SEARCH_PATHS = ".. ../../include/**"; + }; + name = Debug; + }; + 1DEB923708733DC60010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_THREADSAFE_STATICS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + USER_HEADER_SEARCH_PATHS = ".. ../../include/**"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "BenchTool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB923208733DC60010E9CD /* Debug */, + 1DEB923308733DC60010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "BenchTool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB923608733DC60010E9CD /* Debug */, + 1DEB923708733DC60010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/bench/RectBench.cpp b/bench/RectBench.cpp index 3874bb3..0c3eb56 100644 --- a/bench/RectBench.cpp +++ b/bench/RectBench.cpp @@ -31,14 +31,14 @@ public: fColors[i] = rand.nextU() | 0xFF808080; } } - + SkString fName; const char* computeName(const char root[]) { fName.set(root); fName.appendS32(fShift); return fName.c_str(); } - + protected: virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) { c->drawRect(r, p); @@ -82,7 +82,7 @@ public: SkCanvas::PointMode fMode; const char* fName; - PointsBench(void* param, SkCanvas::PointMode mode, const char* name) : + PointsBench(void* param, SkCanvas::PointMode mode, const char* name) : RectBench(param, 2), fMode(mode) { fName = name; } @@ -105,8 +105,7 @@ protected: for (size_t i = 0; i < sizes; i++) { paint.setStrokeWidth(gSizes[i]); this->setupPaint(&paint); - canvas->drawPoints(fMode, N * 2, - reinterpret_cast<const SkPoint*>(fRects), paint); + canvas->drawPoints(fMode, N * 2, SkTCast<SkPoint*>(fRects), paint); paint.setColor(fColors[i]); } } diff --git a/bench/TextBench.cpp b/bench/TextBench.cpp index 5caaddb..f2b9604 100644 --- a/bench/TextBench.cpp +++ b/bench/TextBench.cpp @@ -22,15 +22,16 @@ class TextBench : public SkBenchmark { SkPoint* fPos; SkString fText; SkString fName; - enum { N = 300 }; + enum { N = 600 }; public: TextBench(void* param, const char text[], int ps, bool linearText, - bool posText) : INHERITED(param) { + bool posText, SkColor color = SK_ColorBLACK) : INHERITED(param) { fText.set(text); fPaint.setAntiAlias(true); fPaint.setTextSize(SkIntToScalar(ps)); fPaint.setLinearText(linearText); + fPaint.setColor(color); if (posText) { SkAutoTArray<SkScalar> storage(fText.size()); @@ -47,7 +48,7 @@ public: fPos = NULL; } } - + virtual ~TextBench() { delete[] fPos; } @@ -61,6 +62,10 @@ protected: if (fPos) { fName.append("_pos"); } + + if (SK_ColorBLACK != fPaint.getColor()) { + fName.appendf("_%02X", fPaint.getAlpha()); + } return fName.c_str(); } @@ -70,24 +75,21 @@ protected: SkPaint paint(fPaint); this->setupPaint(&paint); + paint.setColor(fPaint.getColor()); // need our specified color const SkScalar x0 = SkIntToScalar(-10); const SkScalar y0 = SkIntToScalar(-10); - const SkColor colors[] = { SK_ColorBLACK, SK_ColorGRAY }; - - for (size_t j = 0; j < SK_ARRAY_COUNT(colors); j++) { - paint.setColor(colors[j]); - for (int i = 0; i < N; i++) { - SkScalar x = x0 + rand.nextUScalar1() * dim.fX; - SkScalar y = y0 + rand.nextUScalar1() * dim.fY; - if (fPos) { - canvas->save(SkCanvas::kMatrix_SaveFlag); - canvas->translate(x, y); - canvas->drawPosText(fText.c_str(), fText.size(), fPos, paint); - canvas->restore(); - } else { - canvas->drawText(fText.c_str(), fText.size(), x, y, paint); - } + + for (int i = 0; i < N; i++) { + SkScalar x = x0 + rand.nextUScalar1() * dim.fX; + SkScalar y = y0 + rand.nextUScalar1() * dim.fY; + if (fPos) { + canvas->save(SkCanvas::kMatrix_SaveFlag); + canvas->translate(x, y); + canvas->drawPosText(fText.c_str(), fText.size(), fPos, paint); + canvas->restore(); + } else { + canvas->drawText(fText.c_str(), fText.size(), x, y, paint); } } } @@ -103,19 +105,31 @@ private: #define BIG 48 static SkBenchmark* Fact0(void* p) { return new TextBench(p, STR, SMALL, false, false); } +static SkBenchmark* Fact01(void* p) { return new TextBench(p, STR, SMALL, false, false, 0xFFFF0000); } +static SkBenchmark* Fact02(void* p) { return new TextBench(p, STR, SMALL, false, false, 0x88FF0000); } + static SkBenchmark* Fact1(void* p) { return new TextBench(p, STR, SMALL, false, true); } static SkBenchmark* Fact2(void* p) { return new TextBench(p, STR, SMALL, true, false); } static SkBenchmark* Fact3(void* p) { return new TextBench(p, STR, SMALL, true, true); } + static SkBenchmark* Fact4(void* p) { return new TextBench(p, STR, BIG, false, false); } +static SkBenchmark* Fact41(void* p) { return new TextBench(p, STR, BIG, false, false, 0xFFFF0000); } +static SkBenchmark* Fact42(void* p) { return new TextBench(p, STR, BIG, false, false, 0x88FF0000); } + static SkBenchmark* Fact5(void* p) { return new TextBench(p, STR, BIG, false, true); } static SkBenchmark* Fact6(void* p) { return new TextBench(p, STR, BIG, true, false); } static SkBenchmark* Fact7(void* p) { return new TextBench(p, STR, BIG, true, true); } static BenchRegistry gReg0(Fact0); +static BenchRegistry gReg01(Fact01); +static BenchRegistry gReg02(Fact02); static BenchRegistry gReg1(Fact1); static BenchRegistry gReg2(Fact2); static BenchRegistry gReg3(Fact3); static BenchRegistry gReg4(Fact4); +static BenchRegistry gReg41(Fact41); +static BenchRegistry gReg42(Fact42); static BenchRegistry gReg5(Fact5); static BenchRegistry gReg6(Fact6); static BenchRegistry gReg7(Fact7); + diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp index 2f8b006..3b6dfdf 100644 --- a/bench/benchmain.cpp +++ b/bench/benchmain.cpp @@ -368,6 +368,11 @@ int main (int argc, char * const argv[]) { performRotate(&canvas, dim.fX, dim.fY); } + if (repeatDraw > 1) { + SkAutoCanvasRestore acr(&canvas, true); + bench->draw(&canvas); + } + SkMSec now = SkTime::GetMSecs(); for (int i = 0; i < repeatDraw; i++) { SkCanvas* c = &canvas; diff --git a/experimental/SkSetPoly3To3.cpp b/experimental/SkSetPoly3To3.cpp deleted file mode 100644 index a19c5e5..0000000 --- a/experimental/SkSetPoly3To3.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "SkMatrix.h" - -static void computeOuterProduct(SkScalar op[4], - const SkPoint pts0[3], const SkPoint& ave0, - const SkPoint pts1[3], const SkPoint& ave1) { - sk_bzero(op, 4 * sizeof(op[0])); - for (int i = 0; i < 3; i++) { - SkScalar x0 = pts0[i].fX - ave0.fX; - SkScalar y0 = pts0[i].fY - ave0.fY; - SkScalar x1 = pts1[i].fX - ave1.fX; - SkScalar y1 = pts1[i].fY - ave1.fY; - op[0] += SkScalarMul(x0, x1); - op[1] += SkScalarMul(x0, y1); - op[2] += SkScalarMul(y0, x1); - op[3] += SkScalarMul(y0, y1); - } -} - -static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) { - return SkScalarMul(ax, bx) + SkScalarMul(ay, by); -} - -bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) { - const SkPoint& srcAve = src[0]; - const SkPoint& dstAve = dst[0]; - - SkScalar srcOP[4], dstOP[4]; - - computeOuterProduct(srcOP, src, srcAve, src, srcAve); - computeOuterProduct(dstOP, src, srcAve, dst, dstAve); - - SkScalar det = SkScalarMul(srcOP[0], srcOP[3]) - SkScalarMul(srcOP[1], srcOP[2]); - - // need SkScalarNearlyZeroSquared for this (to match Chrome's fix) - if (SkScalarNearlyZero(det)) { - return false; - } - - SkScalar invDet = SkScalarInvert(det); - - // now compute invDet * [srcOP]T * [dstOP] - - // scale and transpose - const SkScalar srcOP0 = SkScalarMul( srcOP[3], invDet); - const SkScalar srcOP1 = SkScalarMul(-srcOP[1], invDet); - const SkScalar srcOP2 = SkScalarMul(-srcOP[2], invDet); - const SkScalar srcOP3 = SkScalarMul( srcOP[0], invDet); - - matrix->reset(); - matrix->setScaleX(dot(srcOP0, srcOP1, dstOP[0], dstOP[2])); - matrix->setSkewX( dot(srcOP2, srcOP3, dstOP[0], dstOP[2])); - matrix->setSkewY (dot(srcOP0, srcOP1, dstOP[1], dstOP[3])); - matrix->setScaleY(dot(srcOP2, srcOP3, dstOP[1], dstOP[3])); - matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY, - matrix->getScaleX(), matrix->getSkewX())); - matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY, - matrix->getSkewY(), matrix->getScaleY())); - return true; -} - diff --git a/experimental/SkSetPoly3To3_A.cpp b/experimental/SkSetPoly3To3_A.cpp deleted file mode 100644 index d79703b..0000000 --- a/experimental/SkSetPoly3To3_A.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "SkMatrix.h" - -#ifdef SK_SCALAR_IS_FIXED - typedef int64_t SkDScalar; - - static SkScalar SkDScalar_toScalar(SkDScalar value) { - SkDScalar result = (value + (1 << 15)) >> 16; - int top = result >> 31; - SkASSERT(top == 0 || top == -1); - return (SkScalar)result; - } - static SkScalar div(SkDScalar numer, SkDScalar denom) { - denom >>= 16; - return numer / denom; - } -#else - typedef double SkDScalar; - - static SkScalar SkDScalar_toScalar(SkDScalar value) { - return static_cast<float>(value); - } - static SkScalar div(SkDScalar numer, SkDScalar denom) { - return static_cast<float>(numer / denom); - } -#endif - -static SkDScalar SkDScalar_setMul(SkScalar a, SkScalar b) { - return (SkDScalar)a * b; -} - -static void computeOuterProduct(SkScalar op[4], - const SkPoint pts0[3], const SkPoint& ave0, - const SkPoint pts1[3], const SkPoint& ave1) { - sk_bzero(op, 4 * sizeof(op[0])); - for (int i = 0; i < 3; i++) { - SkScalar x0 = pts0[i].fX - ave0.fX; - SkScalar y0 = pts0[i].fY - ave0.fY; - SkScalar x1 = pts1[i].fX - ave1.fX; - SkScalar y1 = pts1[i].fY - ave1.fY; - op[0] += SkScalarMul(x0, x1); - op[1] += SkScalarMul(x0, y1); - op[2] += SkScalarMul(y0, x1); - op[3] += SkScalarMul(y0, y1); - } -} - -static SkDScalar ddot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) { - return SkDScalar_setMul(ax, bx) + SkDScalar_setMul(ay, by); -} - -static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) { - return SkDScalar_toScalar(ddot(ax, ay, bx, by)); -} - -bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) { - const SkPoint& srcAve = src[0]; - const SkPoint& dstAve = dst[0]; - - SkScalar srcOP[4], dstOP[4]; - - computeOuterProduct(srcOP, src, srcAve, src, srcAve); - computeOuterProduct(dstOP, src, srcAve, dst, dstAve); - - SkDScalar det = SkDScalar_setMul(srcOP[0], srcOP[3]) - - SkDScalar_setMul(srcOP[1], srcOP[2]); - - SkDScalar M[4]; - - const SkScalar srcOP0 = srcOP[3]; - const SkScalar srcOP1 = -srcOP[1]; - const SkScalar srcOP2 = -srcOP[2]; - const SkScalar srcOP3 = srcOP[0]; - - M[0] = ddot(srcOP0, srcOP1, dstOP[0], dstOP[2]); - M[1] = ddot(srcOP2, srcOP3, dstOP[0], dstOP[2]); - M[2] = ddot(srcOP0, srcOP1, dstOP[1], dstOP[3]); - M[3] = ddot(srcOP2, srcOP3, dstOP[1], dstOP[3]); - - matrix->reset(); - matrix->setScaleX(div(M[0], det)); - matrix->setSkewX( div(M[1], det)); - matrix->setSkewY (div(M[2], det)); - matrix->setScaleY(div(M[3], det)); - matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY, - matrix->getScaleX(), matrix->getSkewX())); - matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY, - matrix->getSkewY(), matrix->getScaleY())); - return true; -} - diff --git a/experimental/SkSetPoly3To3_D.cpp b/experimental/SkSetPoly3To3_D.cpp deleted file mode 100644 index 47c0946..0000000 --- a/experimental/SkSetPoly3To3_D.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "SkMatrix.h" - -typedef int64_t SkDScalar; - -static SkScalar SkDScalar_toScalar(SkDScalar value) { - SkDScalar result = (value + (1 << 15)) >> 16; - int top = result >> 31; - SkASSERT(top == 0 || top == -1); - return (SkScalar)result; -} - -static SkDScalar SkDScalar_setMul(SkScalar a, SkScalar b) { - return (SkDScalar)a * b; -} - -static void computeOuterProduct(SkMatrix* matrix, - const SkPoint pts0[3], const SkPoint& ave0, - const SkPoint pts1[3], const SkPoint& ave1) { - SkDScalar tmp[4]; - sk_bzero(tmp, sizeof(tmp)); - - for (int i = 0; i < 3; i++) { - SkScalar x0 = pts0[i].fX - ave0.fX; - SkScalar y0 = pts0[i].fY - ave0.fY; - SkScalar x1 = pts1[i].fX - ave1.fX; - SkScalar y1 = pts1[i].fY - ave1.fY; - tmp[0] += SkDScalar_setMul(x0, x1); - tmp[1] += SkDScalar_setMul(x0, y1); - tmp[2] += SkDScalar_setMul(y0, x1); - tmp[3] += SkDScalar_setMul(y0, y1); - } - matrix->reset(); - matrix->setScaleX(SkDScalar_toScalar(tmp[0])); - matrix->setSkewY( SkDScalar_toScalar(tmp[1])); - matrix->setSkewX( SkDScalar_toScalar(tmp[2])); - matrix->setScaleY(SkDScalar_toScalar(tmp[3])); -} - -static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) { - return SkDScalar_toScalar(SkDScalar_setMul(ax, bx) + - SkDScalar_setMul(ay, by)); -} - -bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) { - const SkPoint& srcAve = src[0]; - const SkPoint& dstAve = dst[0]; - - SkMatrix srcOP, dstOP; - - computeOuterProduct(&srcOP, src, srcAve, src, srcAve); - - if (!srcOP.invert(&srcOP)) { - return false; - } - - computeOuterProduct(&dstOP, src, srcAve, dst, dstAve); - - matrix->setConcat(dstOP, srcOP); - matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY, - matrix->getScaleX(), matrix->getSkewX())); - matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY, - matrix->getSkewY(), matrix->getScaleY())); - return true; -} - diff --git a/gm/Android.mk b/gm/Android.mk index 6ce1dae..6861e16 100644 --- a/gm/Android.mk +++ b/gm/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ filltypes.cpp \ - gradients.cpp \ + gradients.cpp \ tilemodes.cpp \ bitmapfilters.cpp \ xfermodes.cpp \ @@ -12,7 +12,8 @@ LOCAL_SRC_FILES := \ # additional optional class for this tool LOCAL_SRC_FILES += \ - ../src/utils/SkUnitMappers.cpp + ../src/utils/SkUnitMappers.cpp \ + ../src/utils/SkEGLContext_none.cpp LOCAL_SHARED_LIBRARIES := libcutils libskia LOCAL_C_INCLUDES := \ @@ -20,7 +21,9 @@ LOCAL_C_INCLUDES := \ external/skia/include/core \ external/skia/include/images \ external/skia/include/utils \ - external/skia/include/effects + external/skia/include/effects \ + external/skia/gpu/include \ + external/skia/include/gpu #LOCAL_CFLAGS := diff --git a/gm/blurs.cpp b/gm/blurs.cpp new file mode 100644 index 0000000..8250d76 --- /dev/null +++ b/gm/blurs.cpp @@ -0,0 +1,92 @@ +#include "gm.h" +#include "SkBlurMaskFilter.h" + +namespace skiagm { + +class BlursGM : public GM { +public: + BlursGM() {} + +protected: + virtual SkString onShortName() { + return SkString("blurs"); + } + + virtual SkISize onISize() { + return make_isize(700, 500); + } + + 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, + 200 + gRecs[i].fCy*100, 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 GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new BlursGM; } +static GMRegistry reg(MyFactory); + +} + diff --git a/gm/gm_files.mk b/gm/gm_files.mk index 50eb311..2f7769e 100644 --- a/gm/gm_files.mk +++ b/gm/gm_files.mk @@ -1,8 +1,12 @@ SOURCE := \ bitmapfilters.cpp \ + blurs.cpp \ filltypes.cpp \ gradients.cpp \ + points.cpp \ + poly2poly.cpp \ shapes.cpp \ tilemodes.cpp \ xfermodes.cpp \ + shadertext.cpp \ gmmain.cpp diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp index 59f6c64..357a54e 100644 --- a/gm/gmmain.cpp +++ b/gm/gmmain.cpp @@ -3,6 +3,19 @@ #include "SkGraphics.h" #include "SkImageDecoder.h" #include "SkImageEncoder.h" +#include "SkStream.h" +#include "SkRefCnt.h" + +#include "GrContext.h" +#include "SkGpuCanvas.h" +#include "SkGpuDevice.h" +#include "SkEGLContext.h" +#include "SkDevice.h" + +#ifdef SK_SUPPORT_PDF + #include "SkPDFDevice.h" + #include "SkPDFDocument.h" +#endif using namespace skiagm; @@ -14,7 +27,7 @@ public: Iter() { fReg = GMRegistry::Head(); } - + GM* next() { if (fReg) { GMRegistry::Factory fact = fReg->factory(); @@ -23,7 +36,7 @@ public: } return NULL; } - + static int Count() { const GMRegistry* reg = GMRegistry::Head(); int count = 0; @@ -33,7 +46,7 @@ public: } return count; } - + private: const GMRegistry* fReg; }; @@ -44,12 +57,12 @@ static SkString make_name(const char shortName[], const char configName[]) { return name; } -static SkString make_filename(const char path[], const SkString& name) { +static SkString make_filename(const char path[], const SkString& name, const char suffix[]) { SkString filename(path); if (filename.size() && filename[filename.size() - 1] != '/') { filename.append("/"); } - filename.appendf("%s.png", name.c_str()); + filename.appendf("%s.%s", name.c_str(), suffix); return filename; } @@ -74,8 +87,34 @@ static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) { SkImageEncoder::kPNG_Type, 100); } -static void compare(const SkBitmap& target, const SkBitmap& base, - const SkString& name) { +static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) { + int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1); + int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1); + int db = SkGetPackedB32(c0) - SkGetPackedB32(c1); + return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db)); +} + +static void compute_diff(const SkBitmap& target, const SkBitmap& base, + SkBitmap* diff) { + SkAutoLockPixels alp(*diff); + + const int w = target.width(); + const int h = target.height(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + SkPMColor c0 = *base.getAddr32(x, y); + SkPMColor c1 = *target.getAddr32(x, y); + SkPMColor d = 0; + if (c0 != c1) { + d = compute_diff_pmcolor(c0, c1); + } + *diff->getAddr32(x, y) = d; + } + } +} + +static bool compare(const SkBitmap& target, const SkBitmap& base, + const SkString& name, SkBitmap* diff) { SkBitmap copy; const SkBitmap* bm = ⌖ if (target.config() != SkBitmap::kARGB_8888_Config) { @@ -90,7 +129,7 @@ static void compare(const SkBitmap& target, const SkBitmap& base, if (w != base.width() || h != base.height()) { SkDebugf("---- dimensions mismatch for %s base [%d %d] current [%d %d]\n", name.c_str(), base.width(), base.height(), w, h); - return; + return false; } SkAutoLockPixels bmLock(*bm); @@ -103,27 +142,52 @@ static void compare(const SkBitmap& target, const SkBitmap& base, if (c0 != c1) { SkDebugf("----- pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n", name.c_str(), x, y, c0, c1); - return; + + if (diff) { + diff->setConfig(SkBitmap::kARGB_8888_Config, w, h); + diff->allocPixels(); + compute_diff(*bm, base, diff); + } + return false; } } } + + // they're equal + return true; +} + +static bool write_pdf(const SkString& path, const SkDynamicMemoryWStream& pdf) { + SkFILEWStream stream(path.c_str()); + return stream.write(pdf.getStream(), pdf.getOffset()); } +enum Backend { + kRaster_Backend, + kGPU_Backend, + kPDF_Backend, +}; + static const struct { SkBitmap::Config fConfig; - bool fUsePicture; + Backend fBackend; const char* fName; } gRec[] = { - { SkBitmap::kARGB_8888_Config, false, "8888" }, - { SkBitmap::kARGB_4444_Config, false, "4444" }, - { SkBitmap::kRGB_565_Config, false, "565" }, + { SkBitmap::kARGB_8888_Config, kRaster_Backend, "8888" }, + { SkBitmap::kARGB_4444_Config, kRaster_Backend, "4444" }, + { SkBitmap::kRGB_565_Config, kRaster_Backend, "565" }, + { SkBitmap::kARGB_8888_Config, kGPU_Backend, "gpu" }, +#ifdef SK_SUPPORT_PDF + { SkBitmap::kARGB_8888_Config, kPDF_Backend, "pdf" }, +#endif }; int main (int argc, char * const argv[]) { SkAutoGraphics ag; - + const char* writePath = NULL; // if non-null, where we write the originals 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) char* const* stop = argv + argc; for (++argv; argv < stop; ++argv) { @@ -137,42 +201,106 @@ int main (int argc, char * const argv[]) { if (argv < stop && **argv) { readPath = *argv; } - } + } else if (strcmp(*argv, "-d") == 0) { + argv++; + if (argv < stop && **argv) { + diffPath = *argv; + } + } + } + + // setup a GL context for drawing offscreen + GrContext* context = NULL; + SkEGLContext eglContext; + if (eglContext.init(1024, 1024)) { + context = GrContext::CreateGLShaderContext(); } - + Iter iter; GM* gm; - + + if (readPath) { + fprintf(stderr, "reading from %s\n", readPath); + } else if (writePath) { + fprintf(stderr, "writing to %s\n", writePath); + } + while ((gm = iter.next()) != NULL) { SkISize size = gm->getISize(); - SkDebugf("creating... %s [%d %d]\n", gm->shortName(), + SkDebugf("drawing... %s [%d %d]\n", gm->shortName(), size.width(), size.height()); SkBitmap bitmap; + SkDynamicMemoryWStream pdf; for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { - bitmap.setConfig(gRec[i].fConfig, size.width(), size.height()); - bitmap.allocPixels(); - bitmap.eraseColor(0); - SkCanvas canvas(bitmap); + if (gRec[i].fBackend == kRaster_Backend || + gRec[i].fBackend == kGPU_Backend) { + bitmap.setConfig(gRec[i].fConfig, size.width(), size.height()); + bitmap.allocPixels(); + bitmap.eraseColor(0); + SkCanvas canvas(bitmap); + + if (gRec[i].fBackend == kRaster_Backend) { + gm->draw(&canvas); + } else { // GPU + if (NULL == context) { + continue; + } + SkGpuCanvas gc(context, + SkGpuDevice::Current3DApiRenderTarget()); + gc.setDevice(gc.createDevice(bitmap.config(), + bitmap.width(), + bitmap.height(), + bitmap.isOpaque(), + false))->unref(); + gm->draw(&gc); + gc.readPixels(&bitmap); // overwrite our previous allocation + } + } + // TODO: Figure out a way to compare PDFs. + if (gRec[i].fBackend == kPDF_Backend && writePath) { +#ifdef SK_SUPPORT_PDF + SkISize size = gm->getISize(); + SkPDFDevice* dev = new SkPDFDevice(size.width(), size.height()); + SkAutoUnref aur(dev); + + SkCanvas c(dev); + gm->draw(&c); - gm->draw(&canvas); - + SkPDFDocument doc; + doc.appendPage(dev); + doc.emitPDF(&pdf); +#endif + } SkString name = make_name(gm->shortName(), gRec[i].fName); if (writePath) { - SkString path = make_filename(writePath, name); - bool success = write_bitmap(path, bitmap); - if (!success) { - fprintf(stderr, "FAILED to write %s\n", path.c_str()); + SkString path; + bool success; + if (gRec[i].fBackend != kPDF_Backend) { + path = make_filename(writePath, name, "png"); + success = write_bitmap(path, bitmap); + } else { + path = make_filename(writePath, name, "pdf"); + success = write_pdf(path, pdf); } - } else if (readPath) { - SkString path = make_filename(readPath, name); + if (!success) + fprintf(stderr, "FAILED to write %s\n", path.c_str()); + // TODO: Figure out a way to compare PDFs. + } else if (readPath && gRec[i].fBackend != kPDF_Backend) { + SkString path = make_filename(readPath, name, "png"); SkBitmap orig; bool success = SkImageDecoder::DecodeFile(path.c_str(), &orig, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode, NULL); if (success) { - compare(bitmap, orig, name); + SkBitmap diffBitmap; + success = compare(bitmap, orig, name, diffPath ? &diffBitmap : NULL); + if (!success && diffPath) { + SkString diffName = make_filename(diffPath, name, ".diff.png"); + fprintf(stderr, "Writing %s\n", diffName.c_str()); + write_bitmap(diffName, diffBitmap); + } } else { fprintf(stderr, "FAILED to read %s\n", path.c_str()); } diff --git a/gm/gradients.cpp b/gm/gradients.cpp index aed4e0b..26eee9d 100644 --- a/gm/gradients.cpp +++ b/gm/gradients.cpp @@ -31,7 +31,7 @@ static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data, 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; @@ -50,10 +50,23 @@ static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, 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); + SkShader::TileMode tm, SkUnitMapper* mapper); static const GradMaker gGradMakers[] = { - MakeLinear, MakeRadial, MakeSweep + MakeLinear, MakeRadial, MakeSweep, Make2Radial }; /////////////////////////////////////////////////////////////////////////////// @@ -67,7 +80,7 @@ protected: return SkString("gradients"); } - SkISize onISize() { return make_isize(640, 380); } + SkISize onISize() { return make_isize(640, 510); } void drawBG(SkCanvas* canvas) { canvas->drawColor(0xFFDDDDDD); diff --git a/gm/points.cpp b/gm/points.cpp new file mode 100644 index 0000000..48d8fec --- /dev/null +++ b/gm/points.cpp @@ -0,0 +1,67 @@ +#include "gm.h" +#include "SkRandom.h" + +namespace skiagm { + +class PointsGM : public GM { +public: + PointsGM() {} + +protected: + virtual SkString onShortName() { + return SkString("points"); + } + + virtual SkISize onISize() { + return make_isize(640, 490); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + } + + 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 onDraw(SkCanvas* canvas) { + this->drawBG(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 GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new PointsGM; } +static GMRegistry reg(MyFactory); + +} + diff --git a/gm/poly2poly.cpp b/gm/poly2poly.cpp new file mode 100644 index 0000000..193ede7 --- /dev/null +++ b/gm/poly2poly.cpp @@ -0,0 +1,108 @@ +#include "gm.h" + +namespace skiagm { + +class Poly2PolyGM : public GM { +public: + Poly2PolyGM() {} + +protected: + virtual SkString onShortName() { + return SkString("poly2poly"); + } + + virtual SkISize onISize() { + return make_isize(835, 840); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + } + + 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 onDraw(SkCanvas* canvas) { + this->drawBG(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 GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new Poly2PolyGM; } +static GMRegistry reg(MyFactory); + +} + diff --git a/gm/shadertext.cpp b/gm/shadertext.cpp new file mode 100644 index 0000000..1cf562c --- /dev/null +++ b/gm/shadertext.cpp @@ -0,0 +1,198 @@ +#include "gm.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkUnitMappers.h" + +namespace skiagm { + +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 = 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 ShaderTextGM : public GM { +public: + ShaderTextGM() {} + +protected: + + SkString onShortName() { + return SkString("shadertext"); + } + + SkISize onISize() { return make_isize(950, 500); } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + const char text[] = "Shaded Text"; + const int textLen = SK_ARRAY_COUNT(text) - 1; + static int pointSize = SkIntToScalar(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])->ref(); + canvas->drawText(text, textLen, 0, textBase, paint); + canvas->restore(); + } + canvas->restore(); + } + +private: + typedef GM INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new ShaderTextGM; } +static GMRegistry reg(MyFactory); + +} + + diff --git a/gm/shapes.cpp b/gm/shapes.cpp index b3d4863..324ce7e 100644 --- a/gm/shapes.cpp +++ b/gm/shapes.cpp @@ -53,40 +53,40 @@ public: 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)); } } - + virtual ~ShapesGM() { for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) { SkSafeUnref(fMatrixRefs[i]); } } - + protected: virtual SkString onShortName() { return SkString("shapes"); } - + virtual SkISize onISize() { return make_isize(380, 480); } - + void drawBG(SkCanvas* canvas) { canvas->drawColor(0xFFDDDDDD); } - + 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; SkAutoUnref aur(gs); gs->appendShape(&fGroup); @@ -96,24 +96,25 @@ protected: matrix.setTranslate(SkIntToScalar(240), 0); matrix.preScale(SK_Scalar1*2, SK_Scalar1*2); gs->appendShape(&fGroup, matrix); - -#if 0 + +#if 0 canvas->drawShape(gs); #else - SkPicture pict; - SkCanvas* cv = pict.beginRecording(1000, 1000); + 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(); - canvas->drawPicture(pict); + pict->endRecording(); + canvas->drawPicture(*pict); + pict->unref(); #endif *fMatrixRefs[3] = saveM; } - + private: typedef GM INHERITED; }; diff --git a/gm/xfermodes.cpp b/gm/xfermodes.cpp index 5a43f4a..d417bfa 100644 --- a/gm/xfermodes.cpp +++ b/gm/xfermodes.cpp @@ -34,27 +34,23 @@ static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) { static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF }; class XfermodesGM : public GM { - SkBitmap fBitmap; SkBitmap fBG; SkBitmap fSrcB, fDstB; - void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha) { + void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha, + SkScalar x, SkScalar y) { SkPaint p; - canvas->drawBitmap(fSrcB, 0, 0, &p); + canvas->drawBitmap(fSrcB, x, y, &p); p.setAlpha(alpha); p.setXfermode(mode); - canvas->drawBitmap(fDstB, 0, 0, &p); + canvas->drawBitmap(fDstB, x, y, &p); } public: - XfermodesGM() { - const int W = 64; - const int H = 64; - - fBitmap.setConfig(SkBitmap::kARGB_8888_Config, W, H); - fBitmap.allocPixels(); - + const static int W = 64; + const static int H = 64; + XfermodesGM() { fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4); fBG.setPixels(gBG); fBG.setIsOpaque(true); @@ -68,29 +64,20 @@ protected: } virtual SkISize onISize() { - return make_isize(790, 480); + return make_isize(790, 640); } void drawBG(SkCanvas* canvas) { canvas->drawColor(SK_ColorWHITE); - return; - SkShader* s = SkShader::CreateBitmapShader(fBG, - SkShader::kRepeat_TileMode, - SkShader::kRepeat_TileMode); - SkPaint p; - SkMatrix m; - - p.setShader(s)->unref(); - m.setScale(SkIntToScalar(8), SkIntToScalar(8)); - s->setLocalMatrix(m); - canvas->drawPaint(p); } virtual void onDraw(SkCanvas* canvas) { + canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); + this->drawBG(canvas); const struct { - SkXfermode::Mode fMode; + SkXfermode::Mode fMode; const char* fLabel; } gModes[] = { { SkXfermode::kClear_Mode, "Clear" }, @@ -120,11 +107,8 @@ protected: { SkXfermode::kExclusion_Mode, "Exclusion" }, }; - canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); - - SkCanvas c(fBitmap); - const SkScalar w = SkIntToScalar(fBitmap.width()); - const SkScalar h = SkIntToScalar(fBitmap.height()); + const SkScalar w = SkIntToScalar(W); + const SkScalar h = SkIntToScalar(H); SkShader* s = SkShader::CreateBitmapShader(fBG, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); @@ -135,7 +119,7 @@ protected: SkPaint labelP; labelP.setAntiAlias(true); labelP.setTextAlign(SkPaint::kCenter_Align); - + const int W = 5; SkScalar x0 = 0; @@ -143,27 +127,29 @@ protected: SkScalar x = x0, y = 0; for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) { SkXfermode* mode = SkXfermode::Create(gModes[i].fMode); - - fBitmap.eraseColor(0); - draw_mode(&c, mode, twice ? 0x88 : 0xFF); - SkSafeUnref(mode); - - SkPaint p; + SkAutoUnref aur(mode); SkRect r; r.set(x, y, x+w, y+h); - r.inset(-SK_ScalarHalf, -SK_ScalarHalf); - p.setStyle(SkPaint::kStroke_Style); - canvas->drawRect(r, p); + + SkPaint p; p.setStyle(SkPaint::kFill_Style); p.setShader(s); - r.inset(SK_ScalarHalf, SK_ScalarHalf); canvas->drawRect(r, p); - canvas->drawBitmap(fBitmap, x, y, NULL); + 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; diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h new file mode 100644 index 0000000..c0bbb4b --- /dev/null +++ b/include/config/SkUserConfig.h @@ -0,0 +1,140 @@ +/* + * 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 SkUserConfig_DEFINED +#define SkUserConfig_DEFINED + +/* SkTypes.h, the root of the public header files, does the following trick: + + #include "SkPreConfig.h" + #include "SkUserConfig.h" + #include "SkPostConfig.h" + + SkPreConfig.h runs first, and it is responsible for initializing certain + skia defines. + + SkPostConfig.h runs last, and its job is to just check that the final + defines are consistent (i.e. that we don't have mutually conflicting + defines). + + SkUserConfig.h (this file) runs in the middle. It gets to change or augment + the list of flags initially set in preconfig, and then postconfig checks + that everything still makes sense. + + Below are optional defines that add, subtract, or change default behavior + in Skia. Your port can locally edit this file to enable/disable flags as + you choose, or these can be delared on your command line (i.e. -Dfoo). + + By default, this include file will always default to having all of the flags + commented out, so including it will have no effect. +*/ + +/////////////////////////////////////////////////////////////////////////////// + +/* Scalars (the fractional value type in skia) can be implemented either as + floats or 16.16 integers (fixed). Exactly one of these two symbols must be + defined. +*/ +//#define SK_SCALAR_IS_FLOAT +//#define SK_SCALAR_IS_FIXED + + +/* 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 + can go either way. + */ +//#define SK_CAN_USE_FLOAT + +/* For some performance-critical scalar operations, skia will optionally work + around the standard float operators if it knows that the CPU does not have + native support for floats. If your environment uses software floating point, + define this flag. + */ +//#define SK_SOFTWARE_FLOAT + + +/* Skia has lots of debug-only code. Often this is just null checks or other + parameter checking, but sometimes it can be quite intrusive (e.g. check that + each 32bit pixel is in premultiplied form). This code can be very useful + during development, but will slow things down in a shipping product. + + By default, these mutually exclusive flags are defined in SkPreConfig.h, + based on the presence or absence of NDEBUG, but that decision can be changed + here. + */ +//#define SK_DEBUG +//#define SK_RELEASE + + +/* If, in debugging mode, Skia needs to stop (presumably to invoke a debugger) + it will call SK_CRASH(). If this is not defined it, it is defined in + SkPostConfig.h to write to an illegal address + */ +//#define SK_CRASH() *(int *)(uintptr_t)0 = 0 + + +/* preconfig will have attempted to determine the endianness of the system, + but you can change these mutually exclusive flags here. + */ +//#define SK_CPU_BENDIAN +//#define SK_CPU_LENDIAN + + +/* Some compilers don't support long long for 64bit integers. If yours does + not, define this to the appropriate type. + */ +//#define SkLONGLONG int64_t + + +/* Some envorinments do not suport writable globals (eek!). If yours does not, + define this flag. + */ +//#define SK_USE_RUNTIME_GLOBALS + + +/* To write debug messages to a console, skia will call SkDebugf(...) following + printf conventions (e.g. const char* format, ...). If you want to redirect + this to something other than printf, define yours here + */ +//#define SkDebugf(...) MyFunction(__VA_ARGS__) + +/* To enable additional blitters (and fontscaler code) to support separate + alpha channels for R G B channels, define SK_SUPPORT_LCDTEXT + */ +//#define SK_SUPPORT_LCDTEXT + +/* If zlib is available and you want to support the flate compression + algorithm (used in PDF generation), define SK_ZLIB_INCLUDE to be the + include path. + */ +//#define SK_ZLIB_INCLUDE <zlib.h> + +/* Define this to remove dimension checks on bitmaps. Not all blits will be + correct yet, so this is mostly for debugging the implementation. + */ +//#define SK_ALLOW_OVER_32K_BITMAPS + +/* If SK_DEBUG is defined, then you can optionally define SK_SUPPORT_UNITTEST + which will run additional self-tests at startup. These can take a long time, + so this flag is optional. + */ +#ifdef SK_DEBUG +//#define SK_SUPPORT_UNITTEST +#endif + +#endif + diff --git a/include/config/sk_stdint.h b/include/config/sk_stdint.h new file mode 100644 index 0000000..9a5f5ca --- /dev/null +++ b/include/config/sk_stdint.h @@ -0,0 +1,13 @@ +#ifndef sk_stdint_DEFINED
+#define sk_stdint_DEFINED
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned uint32_t;
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+
+#endif
diff --git a/include/core/SkAdvancedTypefaceMetrics.h b/include/core/SkAdvancedTypefaceMetrics.h new file mode 100644 index 0000000..033e738 --- /dev/null +++ b/include/core/SkAdvancedTypefaceMetrics.h @@ -0,0 +1,136 @@ +/* + * 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 SkAdvancedTypefaceMetrics_DEFINED +#define SkAdvancedTypefaceMetrics_DEFINED + +#include "SkRect.h" +#include "SkRefCnt.h" +#include "SkString.h" +#include "SkTDArray.h" +#include "SkTemplates.h" +#include "SkTScopedPtr.h" + +/** \class SkAdvancedTypefaceMetrics + + The SkAdvancedTypefaceMetrics class is used by the PDF backend to correctly + embed typefaces. This class is filled in with information about a given + typeface by the SkFontHost class. +*/ + +class SkAdvancedTypefaceMetrics : public SkRefCnt { +public: + SkString fFontName; + + enum FontType { + kType1_Font, + kType1CID_Font, + kCFF_Font, + kTrueType_Font, + kOther_Font, + kNotEmbeddable_Font, + }; + // The type of the underlying font program. This field determines which + // of the following fields are valid. If it is kOther_Font or + // kNotEmbeddable_Font, the per glyph information will never be populated. + FontType fType; + + // fMultiMaster may be true for Type1_Font or CFF_Font. + bool fMultiMaster; + uint16_t fLastGlyphID; // The last valid glyph ID in the font. + uint16_t fEmSize; // The size of the em box (defines font units). + + // These enum values match the values used in the PDF file format. + enum StyleFlags { + kFixedPitch_Style = 0x00001, + kSerif_Style = 0x00002, + kSymbolic_Style = 0x00004, + kScript_Style = 0x00008, + kNonsymbolic_Style = 0x00020, + kItalic_Style = 0x00040, + kAllCaps_Style = 0x10000, + kSmallCaps_Style = 0x20000, + kForceBold_Style = 0x40000, + }; + uint16_t fStyle; // Font style characteristics. + int16_t fItalicAngle; // Counterclockwise degrees from vertical of the + // dominant vertical stroke for an Italic face. + // The following fields are all in font units. + int16_t fAscent; // Max height above baseline, not including accents. + int16_t fDescent; // Max depth below baseline (negative). + int16_t fStemV; // Thickness of dominant vertical stem. + int16_t fCapHeight; // Height (from baseline) of top of flat capitals. + + SkIRect fBBox; // The bounding box of all glyphs (in font units). + + template <typename Data> + struct AdvanceMetric { + enum MetricType { + kDefault, // Default advance: fAdvance.count = 1 + kRange, // Advances for a range: fAdvance.count = fEndID-fStartID + kRun, // fStartID-fEndID have same advance: fAdvance.count = 1 + }; + MetricType fType; + uint16_t fStartId; + uint16_t fEndId; + SkTDArray<Data> fAdvance; + SkTScopedPtr<AdvanceMetric<Data> > fNext; + }; + + struct VerticalMetric { + int16_t fVerticalAdvance; + int16_t fOriginXDisp; // Horiz. displacement of the secondary origin. + int16_t fOriginYDisp; // Vert. displacement of the secondary origin. + }; + typedef AdvanceMetric<int16_t> WidthRange; + typedef AdvanceMetric<VerticalMetric> VerticalAdvanceRange; + + // This is indexed by glyph id. + SkTScopedPtr<WidthRange> fGlyphWidths; + // Only used for Vertical CID fonts. + SkTScopedPtr<VerticalAdvanceRange> fVerticalMetrics; + + // The names of each glyph, only populated for postscript fonts. + SkTScopedPtr<SkAutoTArray<SkString> > fGlyphNames; +}; + +namespace skia_advanced_typeface_metrics_utils { + +template <typename Data> +void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range, + int startId); + +template <typename Data> +SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange( + SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot, + int startId); + +template <typename Data> +void finishRange( + SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range, + int endId, + typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType + type); + +template <typename Data, typename FontHandle> +SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData( + FontHandle fontHandle, + int num_glyphs, + bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data)); + +} // namespace skia_advanced_typeface_metrics_utils + +#endif diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h index 1def198..a38cafa 100644 --- a/include/core/SkBitmap.h +++ b/include/core/SkBitmap.h @@ -30,6 +30,9 @@ class SkRegion; class SkFlattenableReadBuffer; class SkFlattenableWriteBuffer; +// This is an opaque class, not interpreted by skia +class SkGpuTexture; + /** \class SkBitmap The SkBitmap class specifies a raster bitmap. A bitmap has an integer width @@ -43,12 +46,22 @@ public: enum Config { kNo_Config, //!< bitmap has not been configured - kA1_Config, //!< 1-bit per pixel, (0 is transparent, 1 is opaque) + /** + * 1-bit per pixel, (0 is transparent, 1 is opaque) + * Valid as a destination (target of a canvas), but not valid as a src. + * i.e. you can draw into a 1-bit bitmap, but you cannot draw from one. + */ + kA1_Config, kA8_Config, //!< 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque) kIndex8_Config, //!< 8-bits per pixel, using SkColorTable to specify the colors kRGB_565_Config, //!< 16-bits per pixel, (see SkColorPriv.h for packing) kARGB_4444_Config, //!< 16-bits per pixel, (see SkColorPriv.h for packing) kARGB_8888_Config, //!< 32-bits per pixel, (see SkColorPriv.h for packing) + /** + * Custom compressed format, not supported on all platforms. + * Cannot be used as a destination (target of a canvas). + * i.e. you may be able to draw from one, but you cannot draw into one. + */ kRLE_Index8_Config, kConfigCount @@ -129,6 +142,12 @@ public: */ size_t getSize() const { return fHeight * fRowBytes; } + /** Return the number of bytes from the pointer returned by getPixels() + to the end of the allocated space in the buffer. Required in + cases where extractBitmap has been called. + */ + size_t getSafeSize() const ; + /** Return the byte size of the pixels, based on the height and rowBytes. This routine is slightly slower than getSize(), but does not truncate the answer to 32bits. @@ -139,6 +158,10 @@ public: return size; } + /** Same as getSafeSize(), but does not truncate the answer to 32bits. + */ + Sk64 getSafeSize64() const ; + /** Returns true if the bitmap is opaque (has no translucent/transparent pixels). */ bool isOpaque() const; @@ -189,6 +212,44 @@ public: */ void setPixels(void* p, SkColorTable* ctable = NULL); + /** Copies the bitmap's pixels to the location pointed at by dst and returns + true if possible, returns false otherwise. + + In the event that the bitmap's stride is equal to dstRowBytes, and if + it is greater than strictly required by the bitmap's current config + (this may happen if the bitmap is an extracted subset of another), then + this function will copy bytes past the eand of each row, excluding the + last row. No copies are made outside of the declared size of dst, + however. + + Always returns false for RLE formats. + + @param dst Location of destination buffer. + @param dstSize Size of destination buffer. Must be large enough to hold + pixels using indicated stride. + @param dstRowBytes Width of each line in the buffer. If -1, uses + bitmap's internal stride. + */ + bool copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes = -1) + const; + + /** Copies the pixels at location src to the bitmap's pixel buffer. + Returns true if copy if possible (bitmap buffer is large enough), + false otherwise. + + Like copyPixelsTo, this function may write values beyond the end of + each row, although never outside the defined buffer. + + Always returns false for RLE formats. + + @param src Location of the source buffer. + @param srcSize Height of source buffer in pixels. + @param srcRowBytes Width of each line in the buffer. If -1, uses i + bitmap's internal stride. + */ + bool copyPixelsFrom(const void* const src, size_t srcSize, + int srcRowBytes = -1); + /** Use the standard HeapAllocator to create the pixelref that manages the pixel memory. It will be sized based on the current width/height/config. If this is called multiple times, a new pixelref object will be created @@ -227,7 +288,7 @@ public: */ bool allocPixels(Allocator* allocator, SkColorTable* ctable); - /** Return the current pixelref object, of any + /** Return the current pixelref object, if any */ SkPixelRef* pixelRef() const { return fPixelRef; } /** Return the offset into the pixelref, if any. Will return 0 if there is @@ -258,10 +319,15 @@ public: */ bool readyToDraw() const { return this->getPixels() != NULL && - ((this->config() != kIndex8_Config && this->config() != kRLE_Index8_Config) || + ((this->config() != kIndex8_Config && + this->config() != kRLE_Index8_Config) || fColorTable != NULL); } + /** Returns the pixelRef's texture, or NULL + */ + SkGpuTexture* getTexture() const; + /** Return the bitmap's colortable (if any). Does not affect the colortable's reference count. */ @@ -477,6 +543,17 @@ private: uint8_t fFlags; uint8_t fBytesPerPixel; // based on config + /* Internal computations for safe size. + */ + static Sk64 ComputeSafeSize64(Config config, + uint32_t width, + uint32_t height, + uint32_t rowBytes); + static size_t ComputeSafeSize(Config config, + uint32_t width, + uint32_t height, + uint32_t rowBytes); + /* Unreference any pixelrefs or colortables */ void freePixels(); diff --git a/src/core/SkBlitRow.h b/include/core/SkBlitRow.h index 7d48b0c..2b652c2 100644 --- a/src/core/SkBlitRow.h +++ b/include/core/SkBlitRow.h @@ -29,6 +29,12 @@ public: const SkPMColor* SK_RESTRICT src, int count, U8CPU alpha, int x, int y); + /** Function pointer that blends a single color with a row of 32-bit colors + onto a 32-bit destination + */ + typedef void (*ColorProc)(SkPMColor* dst, const SkPMColor* src, int count, + SkPMColor color); + //! Public entry-point to return a blit function ptr static Proc Factory(unsigned flags, SkBitmap::Config); @@ -55,15 +61,10 @@ public: into a row of D32 pixels. src and dst may be the same memory, but if they are not, they may not overlap. */ - static void Color32(SkPMColor dst[], const SkPMColor src[], int count, - SkPMColor color); + static void Color32(SkPMColor dst[], const SkPMColor src[], + int count, SkPMColor color); - /** Blend a single color onto a row of 32bit pixels, writing the result - into the same row. - */ - static void Color32(SkPMColor row[], int count, SkPMColor color) { - Color32(row, row, count, color); - } + static ColorProc ColorProcFactory(); /** These static functions are called by the Factory and Factory32 functions, and should return either NULL, or a @@ -74,6 +75,7 @@ public: static Proc32 PlatformProcs32(unsigned flags); static Proc PlatformProcs565(unsigned flags); static Proc PlatformProcs4444(unsigned flags); + static ColorProc PlatformColorProc(); private: enum { diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index b5ccca6..b2a9fa9 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -98,12 +98,46 @@ public: */ SkDevice* setDevice(SkDevice* device); - /** Deprecated - Specify a bitmap for the canvas to draw into. This is a - helper method for setDevice(), and it creates a device for the bitmap by - calling createDevice(). The structure of the bitmap is copied into the - device. - */ - virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap); + /** May be overridden by subclasses. This returns a compatible device + for this canvas, with the specified config/width/height. If the device + is raster, the pixels will be allocated automatically. + */ + virtual SkDevice* createDevice(SkBitmap::Config, int width, int height, + bool isOpaque, bool forLayer = false); + + /** + * Create a new raster device and make it current. This also returns + * the new device. + */ + SkDevice* setBitmapDevice(const SkBitmap& bitmap, bool forLayer = false); + + /** + * Return the current device factory, or NULL. + */ + SkDeviceFactory* getDeviceFactory() const { return fDeviceFactory; } + + /** + * Replace any existing factory with the specified factory. + */ + SkDeviceFactory* setDeviceFactory(SkDeviceFactory*); + + /////////////////////////////////////////////////////////////////////////// + + /** + * Copy the pixels from the device into bitmap. Returns true on success. + * If false is returned, then the bitmap parameter is left unchanged. + * The bitmap parameter is treated as output-only, and will be completely + * overwritten (if the method returns true). + */ + bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap); + bool readPixels(SkBitmap* bitmap); + + /** + * Similar to draw sprite, this method will copy the pixels in bitmap onto + * the device, with the top/left corner specified by (x, y). The pixel + * values in the device are completely replaced: there is no blending. + */ + void writePixels(const SkBitmap& bitmap, int x, int y); /////////////////////////////////////////////////////////////////////////// @@ -139,8 +173,10 @@ public: offscreen bitmap. All drawing calls are directed there, and only when the balancing call to restore() is made is that offscreen transfered to the canvas (or the previous layer). - @param bounds (may be null) the maximum size the offscreen bitmap needs - to be (in local coordinates) + @param bounds (may be null) This rect, if non-null, is used as a hint to + limit the size of the offscreen, and thus drawing may be + clipped to it, though that clipping is not guaranteed to + happen. If exact clipping is desired, use clipRect(). @param paint (may be null) This is copied, and is applied to the offscreen when restore() is called @param flags LayerFlags @@ -153,8 +189,10 @@ public: offscreen bitmap. All drawing calls are directed there, and only when the balancing call to restore() is made is that offscreen transfered to the canvas (or the previous layer). - @param bounds (may be null) the maximum size the offscreen bitmap needs - to be (in local coordinates) + @param bounds (may be null) This rect, if non-null, is used as a hint to + limit the size of the offscreen, and thus drawing may be + clipped to it, though that clipping is not guaranteed to + happen. If exact clipping is desired, use clipRect(). @param alpha This is applied to the offscreen when restore() is called. @param flags LayerFlags @return The value to pass to restoreToCount() to balance this save() @@ -213,12 +251,12 @@ public: @return true if the operation succeeded (e.g. did not overflow) */ virtual bool concat(const SkMatrix& matrix); - + /** Replace the current matrix with a copy of the specified matrix. @param matrix The matrix that will be copied into the current matrix. */ virtual void setMatrix(const SkMatrix& matrix); - + /** Helper for setMatrix(identity). Sets the current matrix to identity. */ void resetMatrix(); @@ -382,7 +420,7 @@ public: details. */ void drawPoint(SkScalar x, SkScalar y, const SkPaint& paint); - + /** Draws a single pixel in the specified color. @param x The X coordinate of which pixel to draw @param y The Y coordiante of which pixel to draw @@ -420,7 +458,7 @@ public: r.set(rect); // promotes the ints to scalars this->drawRect(r, paint); } - + /** Draw the specified rectangle using the specified paint. The rectangle will be filled or framed based on the Style in the paint. @param left The left side of the rectangle to be drawn @@ -508,7 +546,7 @@ public: virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m, const SkPaint* paint = NULL); - + /** Draw the specified bitmap, with its top/left corner at (x,y), NOT transformed by the current matrix. Note: if the paint contains a maskfilter that generates a mask which extends beyond the @@ -535,7 +573,7 @@ public: SkScalar y, const SkPaint& paint); /** Draw the text, with each character/glyph origin specified by the pos[] - array. The origin is interpreted by the Align setting in the paint. + array. The origin is interpreted by the Align setting in the paint. @param text The text to be drawn @param byteLength The number of bytes to read from the text parameter @param pos Array of positions, used to position each character @@ -543,10 +581,10 @@ public: */ virtual void drawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint); - + /** Draw the text, with each character/glyph origin specified by the x coordinate taken from the xpos[] array, and the y from the constY param. - The origin is interpreted by the Align setting in the paint. + The origin is interpreted by the Align setting in the paint. @param text The text to be drawn @param byteLength The number of bytes to read from the text parameter @param xpos Array of x-positions, used to position each character @@ -556,7 +594,7 @@ public: virtual void drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint); - + /** Draw the text, with origin at (x,y), using the specified paint, along the specified path. The paint's Align setting determins where along the path to start the text. @@ -610,7 +648,7 @@ public: canvas. */ virtual void drawPicture(SkPicture& picture); - + /** Draws the specified shape */ virtual void drawShape(SkShape*); @@ -620,7 +658,7 @@ public: kTriangleStrip_VertexMode, kTriangleFan_VertexMode }; - + /** Draw the array of vertices, interpreted as triangles (based on mode). @param vmode How to interpret the array of vertices @param vertexCount The number of points in the vertices array (and @@ -637,7 +675,7 @@ public: @param indices If not null, array of indices to reference into the vertex (texs, colors) array. @param indexCount number of entries in the indices array (if not null) - @param paint Specifies the shader/texture if present. + @param paint Specifies the shader/texture if present. */ virtual void drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], @@ -654,7 +692,7 @@ public: virtual void drawData(const void* data, size_t length); ////////////////////////////////////////////////////////////////////////// - + /** Get the current bounder object. The bounder's reference count is unchaged. @return the canva's bounder (or NULL). @@ -670,13 +708,13 @@ public: @return the set bounder object */ virtual SkBounder* setBounder(SkBounder* bounder); - + /** Get the current filter object. The filter's reference count is not affected. The filter is saved/restored, just like the matrix and clip. @return the canvas' filter (or NULL). */ SkDrawFilter* getDrawFilter() const; - + /** Set the new filter (or NULL). Pass NULL to clear any existing filter. As a convenience, the parameter is returned. If an existing filter exists, its refcnt is decrement. If the new filter is not null, its @@ -701,16 +739,7 @@ public: */ const SkRegion& getTotalClip() const; - /** May be overridden by subclasses. This returns a compatible device - for this canvas, with the specified config/width/height. If isOpaque - is true, then the underlying bitmap is optimized to assume that every - pixel will be drawn to, and thus it does not need to clear the alpha - channel ahead of time (assuming the specified config supports per-pixel - alpha.) If isOpaque is false, then the bitmap should clear its alpha - channel. - */ - virtual SkDevice* createDevice(SkBitmap::Config, int width, int height, - bool isOpaque, bool isForLayer); + void setExternalMatrix(const SkMatrix* = NULL); /////////////////////////////////////////////////////////////////////////// @@ -725,12 +754,12 @@ public: /** Initialize iterator with canvas, and set values for 1st device */ LayerIter(SkCanvas*, bool skipEmptyClips); ~LayerIter(); - + /** Return true if the iterator is done */ bool done() const { return fDone; } /** Cycle to the next device */ void next(); - + // These reflect the current device in the iterator SkDevice* device() const; @@ -739,14 +768,14 @@ public: const SkPaint& paint() const; int x() const; int y() const; - + private: // used to embed the SkDrawIter object directly in our instance, w/o // having to expose that class def to the public. There is an assert // in our constructor to ensure that fStorage is large enough // (though needs to be a compile-time-assert!). We use intptr_t to work // safely with 32 and 64 bit machines (to ensure the storage is enough) - intptr_t fStorage[12]; + intptr_t fStorage[32]; class SkDrawIter* fImpl; // this points at fStorage SkPaint fDefaultPaint; bool fDone; @@ -754,9 +783,9 @@ public: protected: // all of the drawBitmap variants call this guy - virtual void commonDrawBitmap(const SkBitmap&, const SkMatrix& m, - const SkPaint& paint); - + virtual void commonDrawBitmap(const SkBitmap&, const SkIRect*, + const SkMatrix&, const SkPaint& paint); + private: class MCRec; @@ -770,21 +799,21 @@ private: SkDevice* fLastDeviceToGainFocus; SkDeviceFactory* fDeviceFactory; - void prepareForDeviceDraw(SkDevice*); - + void prepareForDeviceDraw(SkDevice*, const SkMatrix&, const SkRegion&); + bool fDeviceCMDirty; // cleared by updateDeviceCMCache() void updateDeviceCMCache(); friend class SkDrawIter; // needs setupDrawForLayerDevice() SkDevice* init(SkDevice*); - void internalDrawBitmap(const SkBitmap&, const SkMatrix& m, + void internalDrawBitmap(const SkBitmap&, const SkIRect*, const SkMatrix& m, const SkPaint* paint); void drawDevice(SkDevice*, int x, int y, const SkPaint*); // shared by save() and saveLayer() int internalSave(SaveFlags flags); void internalRestore(); - + /* These maintain a cache of the clip bounds in local coordinates, (converted to 2s-compliment if floats are slow). */ @@ -816,6 +845,9 @@ private: } } void computeLocalClipBoundsCompareType(EdgeType et) const; + + SkMatrix fExternalMatrix, fExternalInverse; + bool fUseExternalMatrix; }; /** Stack helper class to automatically call restoreToCount() on the canvas diff --git a/include/core/SkChunkAlloc.h b/include/core/SkChunkAlloc.h index 810e7b6..ba9e2c9 100644 --- a/include/core/SkChunkAlloc.h +++ b/include/core/SkChunkAlloc.h @@ -55,7 +55,14 @@ public: size_t unalloc(void* ptr); size_t totalCapacity() const { return fTotalCapacity; } - + + /** + * Returns true if the specified address is within one of the chunks, and + * has at least 1-byte following the address (i.e. if addr points to the + * end of a chunk, then contains() will return false). + */ + bool contains(const void* addr) const; + private: struct Block; Block* fBlock; diff --git a/include/core/SkColor.h b/include/core/SkColor.h index c97a8ec..c17068f 100644 --- a/include/core/SkColor.h +++ b/include/core/SkColor.h @@ -36,13 +36,30 @@ typedef uint32_t SkColor; /** Return a SkColor value from 8 bit component values */ -static inline SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) +static inline SkColor SkColorSetARGBInline(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { SkASSERT(a <= 255 && r <= 255 && g <= 255 && b <= 255); return (a << 24) | (r << 16) | (g << 8) | (b << 0); } +#define SkColorSetARGBMacro(a, r, g, b) \ + static_cast<SkColor>( \ + (static_cast<U8CPU>(a) << 24) | \ + (static_cast<U8CPU>(r) << 16) | \ + (static_cast<U8CPU>(g) << 8) | \ + (static_cast<U8CPU>(b) << 0)) + +/** gcc will generate static initializers for code of this form: + * static const SkColor kMyColor = SkColorSetARGB(0xFF, 0x01, 0x02, 0x03) + * if SkColorSetARGB() is a static inline, but not if it's a macro. + */ +#if defined(NDEBUG) +#define SkColorSetARGB(a, r, g, b) SkColorSetARGBMacro(a, r, g, b) +#else +#define SkColorSetARGB(a, r, g, b) SkColorSetARGBInline(a, r, g, b) +#endif + /** Return a SkColor value from 8 bit component values, with an implied value of 0xFF for alpha (fully opaque) */ diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h index 7c6b7d1..bd407da 100644 --- a/include/core/SkColorPriv.h +++ b/include/core/SkColorPriv.h @@ -28,7 +28,7 @@ /** Turn 0..255 into 0..256 by adding 1 at the half-way point. Used to turn a byte into a scale value, so that we can say scale * value >> 8 instead of alpha * value / 255. - + In debugging, asserts that alpha is 0..255 */ static inline unsigned SkAlpha255To256(U8CPU alpha) { @@ -133,7 +133,7 @@ static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[], int srcScale, int count) { SkASSERT(count > 0); SkASSERT((unsigned)srcScale <= 256); - + srcScale >>= 3; do { @@ -148,7 +148,7 @@ static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[], SkASSERT(SkGetPackedR16(a) + SkGetPackedR16(b) <= SK_R16_MASK); SkASSERT(SkGetPackedG16(a) + SkGetPackedG16(b) <= SK_G16_MASK); SkASSERT(SkGetPackedB16(a) + SkGetPackedB16(b) <= SK_B16_MASK); - + return a + b; } #else @@ -201,7 +201,7 @@ static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[], unsigned r = SkGetPackedR32(c); unsigned g = SkGetPackedG32(c); unsigned b = SkGetPackedB32(c); - + SkA32Assert(a); SkASSERT(r <= a); SkASSERT(g <= a); @@ -211,6 +211,10 @@ static inline void SkBlendRGB16(const uint16_t src[], uint16_t dst[], #define SkPMColorAssert(c) #endif +/** + * Pack the components into a SkPMColor, checking (in the debug version) that + * the components are 0..255, and are already premultiplied (i.e. alpha >= color) + */ static inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { SkA32Assert(a); SkASSERT(r <= a); @@ -221,6 +225,15 @@ static inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT); } +/** + * Same as SkPackARGB32, but this version guarantees to not check that the + * values are premultiplied in the debug version. + */ +static inline SkPMColor SkPackARGB32NoCheck(U8CPU a, U8CPU r, U8CPU g, U8CPU b) { + return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) | + (g << SK_G32_SHIFT) | (b << SK_B32_SHIFT); +} + extern const uint32_t gMask_00FF00FF; static inline uint32_t SkAlphaMulQ(uint32_t c, unsigned scale) { @@ -299,7 +312,7 @@ static inline uint16_t SkDitherPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b) { r = ((r << 1) - ((r >> (8 - SK_R16_BITS) << (8 - SK_R16_BITS)) | (r >> SK_R16_BITS))) >> (8 - SK_R16_BITS); g = ((g << 1) - ((g >> (8 - SK_G16_BITS) << (8 - SK_G16_BITS)) | (g >> SK_G16_BITS))) >> (8 - SK_G16_BITS); b = ((b << 1) - ((b >> (8 - SK_B16_BITS) << (8 - SK_B16_BITS)) | (b >> SK_B16_BITS))) >> (8 - SK_B16_BITS); - + return SkPackRGB16(r, g, b); } @@ -318,7 +331,7 @@ static inline uint32_t SkPMColorToExpanded16x5(SkPMColor c) { unsigned sr = SkPacked32ToR16(c); unsigned sg = SkPacked32ToG16(c); unsigned sb = SkPacked32ToB16(c); - + sr = (sr << 5) | sr; sg = (sg << 5) | (sg >> 1); sb = (sb << 5) | sb; @@ -332,17 +345,17 @@ static inline U16CPU SkSrcOver32To16(SkPMColor src, uint16_t dst) { unsigned sr = SkGetPackedR32(src); unsigned sg = SkGetPackedG32(src); unsigned sb = SkGetPackedB32(src); - + unsigned dr = SkGetPackedR16(dst); unsigned dg = SkGetPackedG16(dst); unsigned db = SkGetPackedB16(dst); - + unsigned isa = 255 - SkGetPackedA32(src); - + dr = (sr + SkMul16ShiftRound(dr, isa, SK_R16_BITS)) >> (8 - SK_R16_BITS); dg = (sg + SkMul16ShiftRound(dg, isa, SK_G16_BITS)) >> (8 - SK_G16_BITS); db = (sb + SkMul16ShiftRound(db, isa, SK_B16_BITS)) >> (8 - SK_B16_BITS); - + return SkPackRGB16(dr, dg, db); } @@ -382,15 +395,15 @@ static inline SkPMColor SkPixel16ToPixel32(U16CPU src) { // similar to SkPixel16ToPixel32, but returns SkColor instead of SkPMColor static inline SkColor SkPixel16ToColor(U16CPU src) { SkASSERT(src == SkToU16(src)); - + unsigned r = SkPacked16ToR32(src); unsigned g = SkPacked16ToG32(src); unsigned b = SkPacked16ToB32(src); - + SkASSERT((r >> (8 - SK_R16_BITS)) == SkGetPackedR16(src)); SkASSERT((g >> (8 - SK_G16_BITS)) == SkGetPackedG16(src)); SkASSERT((b >> (8 - SK_B16_BITS)) == SkGetPackedB16(src)); - + return SkColorSetRGB(r, g, b); } @@ -435,7 +448,7 @@ static inline void SkPMColor16Assert(U16CPU c) { unsigned r = SkGetPackedR4444(c); unsigned g = SkGetPackedG4444(c); unsigned b = SkGetPackedB4444(c); - + SkASSERT(a <= 0xF); SkASSERT(r <= a); SkASSERT(g <= a); @@ -480,7 +493,7 @@ static inline SkPMColor16 SkPackARGB4444(unsigned a, unsigned r, SkASSERT(r <= a); SkASSERT(g <= a); SkASSERT(b <= a); - + return (SkPMColor16)((a << SK_A4444_SHIFT) | (r << SK_R4444_SHIFT) | (g << SK_G4444_SHIFT) | (b << SK_B4444_SHIFT)); } @@ -491,7 +504,7 @@ static inline U16CPU SkAlphaMulQ4(U16CPU c, unsigned scale) { SkASSERT(scale <= 16); const unsigned mask = 0xF0F; //gMask_0F0F; - + #if 0 unsigned rb = ((c & mask) * scale) >> 4; unsigned ag = ((c >> 4) & mask) * scale; @@ -508,7 +521,7 @@ static inline U16CPU SkAlphaMulQ4(U16CPU c, unsigned scale) { */ static inline uint32_t SkExpand_4444(U16CPU c) { SkASSERT(c == (uint16_t)c); - + const unsigned mask = 0xF0F; //gMask_0F0F; return (c & mask) | ((c & ~mask) << 12); } @@ -541,7 +554,7 @@ static inline uint16_t SkSrcOver4444To16(SkPMColor16 s, uint16_t d) { unsigned dr = SkAlphaMul4(SkGetPackedR16(d), scale); unsigned dg = SkAlphaMul4(SkGetPackedG16(d), scale); unsigned db = SkAlphaMul4(SkGetPackedB16(d), scale); - + #if 0 if (sg + dg > 63) { SkDebugf("---- SkSrcOver4444To16 src=%x dst=%x scale=%d, sg=%d dg=%d\n", s, d, scale, sg, dg); @@ -552,13 +565,13 @@ static inline uint16_t SkSrcOver4444To16(SkPMColor16 s, uint16_t d) { static inline uint16_t SkBlend4444To16(SkPMColor16 src, uint16_t dst, int scale16) { SkASSERT((unsigned)scale16 <= 16); - + return SkSrcOver4444To16(SkAlphaMulQ4(src, scale16), dst); } static inline uint16_t SkBlend4444(SkPMColor16 src, SkPMColor16 dst, int scale16) { SkASSERT((unsigned)scale16 <= 16); - + uint32_t src32 = SkExpand_4444(src) * scale16; // the scaled srcAlpha is the bottom byte #ifdef SK_DEBUG @@ -600,7 +613,7 @@ static inline SkPMColor16 SkDitherARGB32To4444(U8CPU a, U8CPU r, r = ((r << 1) - ((r >> 4 << 4) | (r >> 4))) >> 4; g = ((g << 1) - ((g >> 4 << 4) | (g >> 4))) >> 4; b = ((b << 1) - ((b >> 4 << 4) | (b >> 4))) >> 4; - + return SkPackARGB4444(a, r, g, b); } diff --git a/include/core/SkColorShader.h b/include/core/SkColorShader.h index 7c5f941..44a6148 100644 --- a/include/core/SkColorShader.h +++ b/include/core/SkColorShader.h @@ -29,14 +29,16 @@ public: /** Create a ColorShader that will inherit its color from the Paint at draw time. */ - SkColorShader() : fFlags(0), fInheritColor(true) {} + SkColorShader(); /** Create a ColorShader that ignores the color in the paint, and uses the specified color. Note: like all shaders, at draw time the paint's alpha will be respected, and is applied to the specified color. */ - SkColorShader(SkColor c) : fColor(c), fFlags(0), fInheritColor(false) {} - + SkColorShader(SkColor c); + + virtual ~SkColorShader(); + virtual uint32_t getFlags() { return fFlags; } virtual uint8_t getSpan16Alpha() const; virtual bool setContext(const SkBitmap& device, const SkPaint& paint, @@ -45,6 +47,10 @@ public: virtual void shadeSpan16(int x, int y, uint16_t span[], int count); virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count); + virtual BitmapType asABitmap(SkBitmap* outTexture, + SkMatrix* outMatrix, + TileMode xy[2], + SkScalar* twoPointRadialParams); protected: SkColorShader(SkFlattenableReadBuffer& ); virtual void flatten(SkFlattenableWriteBuffer& ); @@ -59,6 +65,9 @@ private: uint16_t fColor16; // cached after setContext() SkBool8 fInheritColor; + // deferred allocation, used for asABitmap() + SkPixelRef* fAsABitmapPixelRef; + typedef SkShader INHERITED; }; diff --git a/include/core/SkDescriptor.h b/include/core/SkDescriptor.h index 8074cff..09397b7 100644 --- a/include/core/SkDescriptor.h +++ b/include/core/SkDescriptor.h @@ -120,13 +120,14 @@ public: return true; } + uint32_t getChecksum() const { return fChecksum; } + struct Entry { uint32_t fTag; uint32_t fLen; }; #ifdef SK_DEBUG - uint32_t getChecksum() const { return fChecksum; } uint32_t getCount() const { return fCount; } #endif diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index dbc8fcf..1b4630d 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -21,6 +21,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkColor.h" +#include "SkRefDict.h" class SkDevice; class SkDraw; @@ -37,26 +38,30 @@ class SkRegion; class SkDeviceFactory { public: virtual ~SkDeviceFactory(); - virtual SkDevice* newDevice(SkBitmap::Config config, int width, int height, - bool isOpaque, bool isForLayer) = 0; + virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width, + int height, bool isOpaque, bool isLayer) = 0; }; class SkRasterDeviceFactory : public SkDeviceFactory { public: - virtual SkDevice* newDevice(SkBitmap::Config config, int width, int height, - bool isOpaque, bool isForLayer); + virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width, + int height, bool isOpaque, bool isLayer); }; class SkDevice : public SkRefCnt { public: - SkDevice(); - /** Construct a new device, extracting the width/height/config/isOpaque - values from the bitmap. Subclasses may override the destructor, which - is virtual, even though this class doesn't have one. SkRefCnt does. + SkDevice(SkCanvas*); + /** Construct a new device, extracting the width/height/config/isOpaque values from + the bitmap. If transferPixelOwnership is true, and the bitmap claims to own its + own pixels (getOwnsPixels() == true), then transfer this responsibility to the + device, and call setOwnsPixels(false) on the bitmap. + + Subclasses may override the destructor, which is virtual, even though this class + doesn't have one. SkRefCnt does. @param bitmap A copy of this bitmap is made and stored in the device */ - SkDevice(const SkBitmap& bitmap); + SkDevice(SkCanvas*, const SkBitmap& bitmap, bool forOffscreen); virtual SkDeviceFactory* getDeviceFactory() { return SkNEW(SkRasterDeviceFactory); @@ -71,10 +76,10 @@ public: /** Return the width of the device (in pixels). */ - int width() const { return fBitmap.width(); } + virtual int width() const { return fBitmap.width(); } /** Return the height of the device (in pixels). */ - int height() const { return fBitmap.height(); } + virtual int height() const { return fBitmap.height(); } /** Return the bitmap config of the device's pixels */ SkBitmap::Config config() const { return fBitmap.getConfig(); } @@ -82,11 +87,11 @@ public: implicitly opaque. */ bool isOpaque() const { return fBitmap.isOpaque(); } - + /** Return the bounds of the device */ void getBounds(SkIRect* bounds) const; - + /** Return true if the specified rectangle intersects the bounds of the device. If sect is not NULL and there is an intersection, sect returns the intersection. @@ -112,6 +117,11 @@ public: virtual void lockPixels(); virtual void unlockPixels(); + /** Return the device's associated texture, or NULL. If returned, it may be + drawn into another device + */ + virtual SkGpuTexture* accessTexture() { return NULL; } + /** Called with the correct matrix and clip before this device is drawn to using those settings. If your subclass overrides this, be sure to call through to the base class as well. @@ -121,7 +131,26 @@ public: /** Called when this device gains focus (i.e becomes the current device for drawing). */ - virtual void gainFocus(SkCanvas*) {} + virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&) {} + + /** Causes any deferred drawing to the device to be completed. + */ + virtual void flush() {} + + /** + * Copy the pixels from the device into bitmap. Returns true on success. + * If false is returned, then the bitmap parameter is left unchanged. + * The bitmap parameter is treated as output-only, and will be completely + * overwritten (if the method returns true). + */ + virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap); + + /** + * Similar to draw sprite, this method will copy the pixels in bitmap onto + * the device, with the top/left corner specified by (x, y). The pixel + * values in the device are completely replaced: there is no blending. + */ + virtual void writePixels(const SkBitmap& bitmap, int x, int y); /** These are called inside the per-device-layer loop for each draw call. When these are called, we have already applied any saveLayer operations, @@ -134,8 +163,11 @@ public: virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint); virtual void drawPath(const SkDraw&, const SkPath& path, - const SkPaint& paint); + const SkPaint& paint, + const SkMatrix* prePathMatrix = NULL, + bool pathIsMutable = false); 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); @@ -158,6 +190,10 @@ public: virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y, const SkPaint&); + /////////////////////////////////////////////////////////////////////////// + + SkRefDict& getRefDict() { return fRefDict; } + protected: /** Update as needed the pixel value in the bitmap, so that the caller can access the pixels directly. Note: only the pixels field should be altered. The config/width/height/rowbytes @@ -165,8 +201,17 @@ protected: */ virtual void onAccessBitmap(SkBitmap*); + SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); } + // just for subclasses, to assign a custom pixelref + SkPixelRef* setPixelRef(SkPixelRef* pr, size_t offset) { + fBitmap.setPixelRef(pr, offset); + return pr; + } + private: - SkBitmap fBitmap; + SkCanvas* fCanvas; + SkBitmap fBitmap; + SkRefDict fRefDict; }; #endif diff --git a/include/core/SkDraw.h b/include/core/SkDraw.h index a86ef67..fb2f292 100644 --- a/include/core/SkDraw.h +++ b/include/core/SkDraw.h @@ -33,12 +33,12 @@ struct SkDrawProcs; class SkDraw { public: - SkDraw() : fDevice(NULL), fBounder(NULL), fProcs(NULL) {} + SkDraw(); SkDraw(const SkDraw& src); void drawPaint(const SkPaint&) const; void drawPoints(SkCanvas::PointMode, size_t count, const SkPoint[], - const SkPaint&) const; + const SkPaint&, bool forceUseDevice = false) const; void drawRect(const SkRect&, const SkPaint&) const; /* To save on mallocs, we allow a flag that tells us that srcPath is mutable, so that we don't have to make copies of it as we transform it. @@ -86,12 +86,18 @@ public: const SkBitmap* fBitmap; // required const SkMatrix* fMatrix; // required const SkRegion* fClip; // required + SkDevice* fDevice; // optional SkBounder* fBounder; // optional SkDrawProcs* fProcs; // optional + const SkMatrix* fMVMatrix; // optional + const SkMatrix* fExtMatrix; // optional + #ifdef SK_DEBUG - void validate() const; + void validate() const; +#else + void validate() const {} #endif }; diff --git a/include/core/SkFixed.h b/include/core/SkFixed.h index be4bf99..2a2456e 100644 --- a/include/core/SkFixed.h +++ b/include/core/SkFixed.h @@ -17,7 +17,7 @@ #ifndef SkFixed_DEFINED #define SkFixed_DEFINED -#include "SkTypes.h" +#include "SkMath.h" /** \file SkFixed.h @@ -39,7 +39,15 @@ typedef int32_t SkFixed; #ifdef SK_CAN_USE_FLOAT #define SkFixedToFloat(x) ((x) * 1.5258789e-5f) +#if 1 #define SkFloatToFixed(x) ((SkFixed)((x) * SK_Fixed1)) +#else + // pins over/under flows to max/min int32 (slower than just a cast) + static inline SkFixed SkFloatToFixed(float x) { + int64_t n = x * SK_Fixed1; + return (SkFixed)n; + } +#endif #define SkFixedToDouble(x) ((x) * 1.5258789e-5) #define SkDoubleToFixed(x) ((SkFixed)((x) * SK_Fixed1)) diff --git a/include/core/SkFlate.h b/include/core/SkFlate.h new file mode 100644 index 0000000..c496b6f --- /dev/null +++ b/include/core/SkFlate.h @@ -0,0 +1,48 @@ +/* + * 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 SkFlate_DEFINED +#define SkFlate_DEFINED + +#include "SkTypes.h" + +class SkDynamicMemoryWStream; +class SkStream; + +/** \class SkFlate + A class to provide access to the flate compression algorithm. +*/ +class SkFlate { +public: + /** Indicates if the flate algorithm is available. + */ + static bool HaveFlate(); + + /** Use the flate compression algorithm to compress the data in src, + putting the result into dst. Returns false if an error occurs. + */ + static bool Deflate(SkStream* src, SkDynamicMemoryWStream* dst); + + /** Use the flate compression algorithm to decompress the data in src, + putting the result into dst. Returns false if an error occurs. + */ + static bool Inflate(SkStream* src, SkDynamicMemoryWStream* dst); + +private: + static const size_t kBufferSize; +}; + +#endif diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h index 84e575f..d70ba65 100644 --- a/include/core/SkFontHost.h +++ b/include/core/SkFontHost.h @@ -59,14 +59,16 @@ typedef uint32_t SkFontTableTag; class SkFontHost { public: /** Return a new, closest matching typeface given either an existing family - (specified by a typeface in that family) or by a familyName, and a - requested style. - 1) If familyFace is null, use famillyName. - 2) If famillyName is null, use familyFace. - 3) If both are null, return the default font that best matches style + (specified by a typeface in that family) or by a familyName and a + requested style, or by a set of Unicode codepoitns to cover in a given + style. + 1) If familyFace is null, use familyName. + 2) If familyName is null, use data (UTF-16 to cover). + 3) If all are null, return the default font that best matches style */ static SkTypeface* CreateTypeface(const SkTypeface* familyFace, - const char famillyName[], + const char familyName[], + const void* data, size_t bytelength, SkTypeface::Style style); /** Return a new typeface given the data buffer. If the data does not @@ -174,6 +176,16 @@ public: /////////////////////////////////////////////////////////////////////////// + /** Retrieve detailed typeface metrics. Used by the PDF backend. + @param perGlyphInfo Indicate if the glyph specific information. + @param perGlyphInfo Indicate if the glyph specific information (advances + and names) should be populated. + @return The returned object has already been referenced. NULL is + returned if the font is not found. + */ + static SkAdvancedTypefaceMetrics* GetAdvancedTypefaceMetrics( + SkFontID fontID, bool perGlyphInfo); + /** Return the number of tables in the font */ static int CountTables(SkFontID); diff --git a/include/core/SkGeometry.h b/include/core/SkGeometry.h index b8ab74c..a209783 100644 --- a/include/core/SkGeometry.h +++ b/include/core/SkGeometry.h @@ -26,10 +26,13 @@ */ typedef SkPoint SkXRay; -/** Given a line segment from pts[0] to pts[1], and ax xray, return true if - they intersect. +/** Given a line segment from pts[0] to pts[1], and an xray, return true if + they intersect. Optional outgoing "ambiguous" argument indicates + whether the answer is ambiguous because the query occurred exactly at + one of the endpoints' y coordinates, indicating that another query y + coordinate is preferred for robustness. */ -bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2]); +bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2], bool* ambiguous = NULL); /** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the equation. @@ -155,8 +158,12 @@ int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tV left of the curve, the line is not considered to cross the curve, but if it is equal to cubic[3].fY then it is considered to cross. + Optional outgoing "ambiguous" argument indicates whether the answer is + ambiguous because the query occurred exactly at one of the endpoints' y + coordinates, indicating that another query y coordinate is preferred + for robustness. */ -bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]); +bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous = NULL); /** Given an arbitrary cubic bezier, return the number of times an xray crosses the cubic. Valid return values are [0..3] @@ -165,8 +172,12 @@ bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]); left of the curve, the line is not considered to cross the curve, but if it is equal to cubic[3].fY then it is considered to cross. + Optional outgoing "ambiguous" argument indicates whether the answer is + ambiguous because the query occurred exactly at one of the endpoints' y + coordinates or at a tangent point, indicating that another query y + coordinate is preferred for robustness. */ -int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4]); +int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous = NULL); /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/core/SkMallocPixelRef.h b/include/core/SkMallocPixelRef.h index 1c36b74..7022ba2 100644 --- a/include/core/SkMallocPixelRef.h +++ b/include/core/SkMallocPixelRef.h @@ -25,13 +25,15 @@ class SkMallocPixelRef : public SkPixelRef { public: /** Allocate the specified buffer for pixels. The memory is freed when the - last owner of this pixelref is gone. + last owner of this pixelref is gone. If addr is NULL, sk_malloc_throw() + is called to allocate it. */ SkMallocPixelRef(void* addr, size_t size, SkColorTable* ctable); virtual ~SkMallocPixelRef(); //! Return the allocation size for the pixels size_t getSize() const { return fSize; } + void* getAddr() const { return fStorage; } // overrides from SkPixelRef virtual void flatten(SkFlattenableWriteBuffer&) const; @@ -58,4 +60,5 @@ private: typedef SkPixelRef INHERITED; }; + #endif diff --git a/include/core/SkMask.h b/include/core/SkMask.h index 608010d..58a2493 100644 --- a/include/core/SkMask.h +++ b/include/core/SkMask.h @@ -44,8 +44,9 @@ struct SkMask { edges. kVerticalLCD_Format has an extra row at the top and bottom. */ - kHorizontalLCD_Format, //!< 4 bytes/pixel: a/r/g/b - kVerticalLCD_Format, //!< 4 bytes/pixel: a/r/g/b + kHorizontalLCD_Format, //!< 4 bytes/pixel: a/r/g/b + kVerticalLCD_Format, //!< 4 bytes/pixel: a/r/g/b + kARGB32_Format, //!< SkPMColor }; enum { diff --git a/include/core/SkMath.h b/include/core/SkMath.h index e0f2361..af19083 100644 --- a/include/core/SkMath.h +++ b/include/core/SkMath.h @@ -161,6 +161,13 @@ static inline int SkNextLog2(uint32_t value) { return 32 - SkCLZ(value - 1); } +/** Returns true if value is a power of 2. Does not explicitly check for + value <= 0. + */ +static inline bool SkIsPow2(int value) { + return (value & (value - 1)) == 0; +} + /////////////////////////////////////////////////////////////////////////////// /** SkMulS16(a, b) multiplies a * b, but requires that a and b are both int16_t. @@ -213,6 +220,16 @@ static inline U8CPU SkMulDiv255Round(U8CPU a, U8CPU b) { return (prod + (prod >> 8)) >> 8; } +/** Return (a*b)/255, taking the ceiling of any fractional bits. Only valid if + both a and b are 0..255. The expected result equals (a * b + 254) / 255. + */ +static inline U8CPU SkMulDiv255Ceiling(U8CPU a, U8CPU b) { + SkASSERT((uint8_t)a == a); + SkASSERT((uint8_t)b == b); + unsigned prod = SkMulS16(a, b) + 255; + return (prod + (prod >> 8)) >> 8; +} + /** Return a*b/((1 << shift) - 1), rounding any fractional bits. Only valid if a and b are unsigned and <= 32767 and shift is > 0 and <= 8 */ diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h index 7ef7d8c..5c82d78 100644 --- a/include/core/SkMatrix.h +++ b/include/core/SkMatrix.h @@ -292,6 +292,13 @@ public: */ bool invert(SkMatrix* inverse) const; + /** Fills the passed array with the tranform values in the right order + for PDFs. If the matrix is a perspective transform, returns false + and fills the array with an identity transform. + @param transform The array to fill in. + */ + bool pdfTransform(SkScalar transform[6]) const; + /** Apply this matrix to the array of points specified by src, and write the transformed points into the array of points specified by dst. dst[] = M * src[] diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index f2df226..3c847de 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -113,10 +113,11 @@ public: kDevKernText_Flag = 0x100, //!< mask to enable device kerning text kLCDRenderText_Flag = 0x200, //!< mask to enable subpixel glyph renderering kEmbeddedBitmapText_Flag = 0x400, //!< mask to enable embedded bitmap strikes + kAutoHinting_Flag = 0x800, //!< mask to force Freetype's autohinter // when adding extra flags, note that the fFlags member is specified // with a bit-width and you'll have to expand it. - kAllFlags = 0x7FF + kAllFlags = 0xFFF }; /** Return the paint's flags. Use the Flag enum to test flag values. @@ -205,6 +206,18 @@ public: */ void setEmbeddedBitmapText(bool useEmbeddedBitmapText); + bool isAutohinted() const + { + return SkToBool(this->getFlags() & kAutoHinting_Flag); + } + + /** Helper for setFlags(), setting or clearing the kAutoHinting_Flag bit + @param useAutohinter true to set the kEmbeddedBitmapText bit in the + paint's flags, + false to clear it. + */ + void setAutohinted(bool useAutohinter); + /** Helper for getFlags(), returning true if kUnderlineText_Flag bit is set @return true if the underlineText bit is set in the paint's flags. */ @@ -831,7 +844,7 @@ private: SkColor fColor; SkScalar fWidth; SkScalar fMiterLimit; - unsigned fFlags : 11; + unsigned fFlags : 12; unsigned fTextAlign : 2; unsigned fCapType : 2; unsigned fJoinType : 2; @@ -859,9 +872,10 @@ private: enum { kCanonicalTextSizeForPaths = 64 }; + friend class SkAutoGlyphCache; friend class SkCanvas; friend class SkDraw; - friend class SkAutoGlyphCache; + friend class SkPDFDevice; friend class SkTextToPathIter; }; diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h index f1dab36..c0259af 100644 --- a/include/core/SkPixelRef.h +++ b/include/core/SkPixelRef.h @@ -25,6 +25,9 @@ class SkMutex; class SkFlattenableReadBuffer; class SkFlattenableWriteBuffer; +// this is an opaque class, not interpreted by skia +class SkGpuTexture; + /** \class SkPixelRef This class is the smart container for pixel memory, and is used with @@ -105,6 +108,10 @@ public: */ void setURI(const SkString& uri) { fURI = uri; } + /** Are we really wrapping a texture instead of a bitmap? + */ + virtual SkGpuTexture* getTexture() { return NULL; } + // serialization typedef SkPixelRef* (*Factory)(SkFlattenableReadBuffer&); diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h index cb6473c..57cc368 100644 --- a/include/core/SkPostConfig.h +++ b/include/core/SkPostConfig.h @@ -107,8 +107,16 @@ #endif #ifndef SK_DEBUGBREAK - #define SK_DEBUGBREAK(cond) do { if (!(cond)) DebugBreak(); } while (false) + #define SK_DEBUGBREAK(cond) do { if (!(cond)) __debugbreak(); } while (false) #endif + + #ifndef SK_A32_SHIFT + #define SK_A32_SHIFT 24 + #define SK_R32_SHIFT 16 + #define SK_G32_SHIFT 8 + #define SK_B32_SHIFT 0 + #endif + #elif defined(SK_BUILD_FOR_MAC) #ifndef SK_DEBUGBREAK #define SK_DEBUGBREAK(cond) do { if (!(cond)) SK_CRASH(); } while (false) diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h index 0a5170c..f907578 100644 --- a/include/core/SkPreConfig.h +++ b/include/core/SkPreConfig.h @@ -17,9 +17,17 @@ #ifndef SkPreConfig_DEFINED #define SkPreConfig_DEFINED +#ifdef WEBKIT_VERSION_MIN_REQUIRED + #include "config.h" +#endif + ////////////////////////////////////////////////////////////////////// -#if !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_SYMBIAN) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_SDL) +#if !defined(SK_BUILD_FOR_ANDROID_NDK) && !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_SYMBIAN) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_SDL) && !defined(SK_BUILD_FOR_BREW) + + #ifdef __APPLE__ + #include "TargetConditionals.h" + #endif #if defined(PALMOS_SDK_VERSION) #define SK_BUILD_FOR_PALM @@ -31,6 +39,12 @@ #define SK_BUILD_FOR_WIN32 #elif defined(linux) #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 diff --git a/include/core/SkRect.h b/include/core/SkRect.h index fbd9f7f..b6caad5 100644 --- a/include/core/SkRect.h +++ b/include/core/SkRect.h @@ -27,6 +27,36 @@ struct SkIRect { int32_t fLeft, fTop, fRight, fBottom; + static SkIRect MakeEmpty() { + SkIRect r; + r.setEmpty(); + return r; + } + + static SkIRect MakeWH(int32_t w, int32_t h) { + SkIRect r; + r.set(0, 0, w, h); + return r; + } + + static SkIRect MakeSize(const SkISize& size) { + SkIRect r; + r.set(0, 0, size.width(), size.height()); + return r; + } + + static SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) { + SkIRect rect; + rect.set(l, t, r, b); + return rect; + } + + static SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) { + SkIRect r; + r.set(x, y, x + w, y + h); + return r; + } + /** Return true if the rectangle's width or height are <= 0 */ bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } @@ -266,6 +296,7 @@ struct SkRect { /** Return true if the rectangle's width or height are <= 0 */ bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } + bool hasValidCoordinates() const; SkScalar width() const { return fRight - fLeft; } SkScalar height() const { return fBottom - fTop; } SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } diff --git a/include/core/SkRefCnt.h b/include/core/SkRefCnt.h index 7e325e0..f109ead 100644 --- a/include/core/SkRefCnt.h +++ b/include/core/SkRefCnt.h @@ -62,24 +62,6 @@ public: SkDELETE(this); } } - - /** Helper version of ref(), that first checks to see if this is not null. - If this is null, then do nothing. - */ - void safeRef() const { - if (this) { - this->ref(); - } - } - - /** Helper version of unref(), that first checks to see if this is not null. - If this is null, then do nothing. - */ - void safeUnref() const { - if (this) { - this->unref(); - } - } private: mutable int32_t fRefCnt; @@ -147,5 +129,51 @@ template <typename T> static inline void SkSafeUnref(T* obj) { } } +/** Wrapper class for SkRefCnt pointers. This manages ref/unref of a pointer to + a SkRefCnt (or subclass) object. + */ +template <typename T> class SkRefPtr { +public: + SkRefPtr() : fObj(NULL) {} + SkRefPtr(T* obj) : fObj(obj) { SkSafeRef(fObj); } + SkRefPtr(const SkRefPtr& o) : fObj(o.fObj) { SkSafeRef(fObj); } + ~SkRefPtr() { SkSafeUnref(fObj); } + + SkRefPtr& operator=(const SkRefPtr& rp) { + SkRefCnt_SafeAssign(fObj, rp.fObj); + return *this; + } + SkRefPtr& operator=(T* obj) { + SkRefCnt_SafeAssign(fObj, obj); + return *this; + } + + bool operator==(const SkRefPtr& rp) const { return fObj == rp.fObj; } + bool operator==(const T* obj) const { return fObj == obj; } + bool operator!=(const SkRefPtr& rp) const { return fObj != rp.fObj; } + bool operator!=(const T* obj) const { return fObj != obj; } + + T* get() const { return fObj; } + T& operator*() const { return *fObj; } + T* operator->() const { return fObj; } + bool operator!() const { return !fObj; } + + typedef T* SkRefPtr::*unspecified_bool_type; + operator unspecified_bool_type() const { return fObj ? &SkRefPtr::fObj : NULL; } + +private: + T* fObj; +}; + +template <typename T> +inline bool operator==(T* obj, const SkRefPtr<T>& rp) { + return obj == rp.get(); +} + +template <typename T> +inline bool operator!=(T* obj, const SkRefPtr<T>& rp) { + return obj != rp.get(); +} + #endif diff --git a/include/core/SkRefDict.h b/include/core/SkRefDict.h new file mode 100644 index 0000000..3ee27d9 --- /dev/null +++ b/include/core/SkRefDict.h @@ -0,0 +1,61 @@ +/* + 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 SkRefDict_DEFINED +#define SkRefDict_DEFINED + +#include "SkRefCnt.h" + +/** + * A dictionary of string,refcnt pairs. The dictionary is also an owner of the + * refcnt objects while they are contained. + */ +class SkRefDict : SkNoncopyable { +public: + SkRefDict(); + ~SkRefDict(); + + /** + * Return the data associated with name[], or NULL if no matching entry + * is found. The reference-count of the entry is not affected. + */ + SkRefCnt* find(const char name[]) const; + + /** + * If data is NULL, remove (if present) the entry matching name and call + * prev_data->unref() on the data for the matching entry. + * If data is not-NULL, replace the existing entry matching name and + * call (prev_data->unref()), or add a new one. In either case, + * data->ref() is called. + */ + void set(const char name[], SkRefCnt* data); + + /** + * Remove the matching entry (if found) and unref its data. + */ + void remove(const char name[]) { this->set(name, NULL); } + + /** + * Remove all entries, and unref() their associated data. + */ + void removeAll(); + +private: + struct Impl; + Impl* fImpl; +}; + +#endif diff --git a/include/core/SkRegion.h b/include/core/SkRegion.h index 09b835d..d8c4e00 100644 --- a/include/core/SkRegion.h +++ b/include/core/SkRegion.h @@ -98,6 +98,13 @@ public: */ bool setRect(int32_t left, int32_t top, int32_t right, int32_t bottom); + /** Set this region to the union of an array of rects. This is generally + faster than calling region.op(rect, kUnion_Op) in a loop. If count is + 0, then this region is set to the empty region. + @return true if the resulting region is non-empty + */ + bool setRects(const SkIRect rects[], int count); + /** Set this region to the specified region, and return true if it is non-empty. */ bool setRegion(const SkRegion&); @@ -260,7 +267,8 @@ public: bool done() { return fDone; } void next(); const SkIRect& rect() const { return fRect; } - + // may return null + const SkRegion* rgn() const { return fRgn; } private: const SkRegion* fRgn; const RunType* fRuns; diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h index 9130a7c..09f24b5 100644 --- a/include/core/SkScalar.h +++ b/include/core/SkScalar.h @@ -60,6 +60,12 @@ /** SkScalarIsNaN(n) returns true if argument is not a number */ static inline bool SkScalarIsNaN(float x) { return x != x; } + /** Returns true if x is not NaN and not infinite */ + static inline bool SkScalarIsFinite(float x) { + uint32_t bits = SkFloat2Bits(x); // need unsigned for our shifts + int exponent = bits << 1 >> 24; + return exponent != 0xFF; + } /** SkIntToScalar(n) returns its integer argument as an SkScalar */ #define SkIntToScalar(n) ((float)(n)) @@ -177,6 +183,8 @@ #define SK_ScalarMin SK_FixedMin #define SK_ScalarNaN SK_FixedNaN #define SkScalarIsNaN(x) ((x) == SK_FixedNaN) + #define SkScalarIsFinite(x) ((x) != SK_FixedNaN) + #define SkIntToScalar(n) SkIntToFixed(n) #define SkFixedToScalar(x) (x) #define SkScalarToFixed(x) (x) @@ -253,5 +261,18 @@ static inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) { return A + SkScalarMul(B - A, t); } +/** Interpolate along the function described by (keys[length], values[length]) + for the passed searchKey. SearchKeys outside the range keys[0]-keys[Length] + clamp to the min or max value. This function was inspired by a desire + to change the multiplier for thickness in fakeBold; therefore it assumes + the number of pairs (length) will be small, and a linear search is used. + Repeated keys are allowed for discontinuous functions (so long as keys is + monotonically increasing), and if key is the value of a repeated scalar in + keys, the first one will be used. However, that may change if a binary + search is used. +*/ +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 59d7125..0c93e2e 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -29,7 +29,10 @@ class SkPathEffect; class SkRasterizer; // needs to be != to any valid SkMask::Format -#define MASK_FORMAT_JUST_ADVANCE (0xFF) +#define MASK_FORMAT_UNKNOWN (0xFF) +#define MASK_FORMAT_JUST_ADVANCE MASK_FORMAT_UNKNOWN + +#define kMaxGlyphWidth (1<<13) struct SkGlyph { void* fImage; @@ -42,25 +45,38 @@ struct SkGlyph { uint8_t fMaskFormat; int8_t fRsbDelta, fLsbDelta; // used by auto-kerning - + + void init(uint32_t id) { + fID = id; + fImage = NULL; + fPath = NULL; + fMaskFormat = MASK_FORMAT_UNKNOWN; +#ifdef SK_GPU_AWARE_GLYPHCACHE + fGLCacheOffset = SKGLYPH_GLCACHEOFFSET_INVALID; + fGLStrikePtr = NULL; +#endif + } + unsigned rowBytes() const { unsigned rb = fWidth; if (SkMask::kBW_Format == fMaskFormat) { rb = (rb + 7) >> 3; + } else if (SkMask::kARGB32_Format == fMaskFormat) { + rb <<= 2; } else { rb = SkAlign4(rb); } return rb; } - + bool isJustAdvance() const { return MASK_FORMAT_JUST_ADVANCE == fMaskFormat; } - + bool isFullMetrics() const { return MASK_FORMAT_JUST_ADVANCE != fMaskFormat; } - + uint16_t getGlyphID() const { return ID2Code(fID); } @@ -70,27 +86,27 @@ struct SkGlyph { SkASSERT(code >= baseGlyphCount); return code - baseGlyphCount; } - + unsigned getSubX() const { return ID2SubX(fID); } - + SkFixed getSubXFixed() const { return SubToFixed(ID2SubX(fID)); } - + SkFixed getSubYFixed() const { return SubToFixed(ID2SubY(fID)); } - + size_t computeImageSize() const; - + /** Call this to set all of the metrics fields to 0 (e.g. if the scaler encounters an error measuring a glyph). Note: this does not alter the fImage, fPath, fID, fMaskFormat fields. */ void zeroMetrics(); - + enum { kSubBits = 2, kSubMask = ((1 << kSubBits) - 1), @@ -104,28 +120,28 @@ struct SkGlyph { static unsigned ID2Code(uint32_t id) { return id & kCodeMask; } - + static unsigned ID2SubX(uint32_t id) { return id >> (kSubShift + kSubShiftX); } - + static unsigned ID2SubY(uint32_t id) { return (id >> (kSubShift + kSubShiftY)) & kSubMask; } - + static unsigned FixedToSub(SkFixed n) { return (n >> (16 - kSubBits)) & kSubMask; } - + static SkFixed SubToFixed(unsigned sub) { SkASSERT(sub <= kSubMask); return sub << (16 - kSubBits); } - + static uint32_t MakeID(unsigned code) { return code; } - + static uint32_t MakeID(unsigned code, SkFixed x, SkFixed y) { SkASSERT(code <= kCodeMask); x = FixedToSub(x); @@ -134,7 +150,7 @@ struct SkGlyph { (y << (kSubShift + kSubShiftY)) | code; } - + void toMask(SkMask* mask) const; /** Given a glyph which is has a mask format of LCD or VerticalLCD, take @@ -156,6 +172,8 @@ public: kHintingBit2_Flag = 0x20, kEmbeddedBitmapText_Flag = 0x40, kEmbolden_Flag = 0x80, + kSubpixelPositioning_Flag = 0x100, + kAutohinting_Flag = 0x200, }; private: enum { @@ -167,10 +185,9 @@ public: SkScalar fTextSize, fPreScaleX, fPreSkewX; SkScalar fPost2x2[2][2]; SkScalar fFrameWidth, fMiterLimit; - bool fSubpixelPositioning; uint8_t fMaskFormat; uint8_t fStrokeJoin; - uint8_t fFlags; + uint16_t fFlags; // Warning: when adding members note that the size of this structure // must be a multiple of 4. SkDescriptor requires that its arguments be // multiples of four and this structure is put in an SkDescriptor in diff --git a/include/core/SkShader.h b/include/core/SkShader.h index f3d4856..1cdbf17 100644 --- a/include/core/SkShader.h +++ b/include/core/SkShader.h @@ -145,12 +145,56 @@ public: virtual void beginSession(); virtual void endSession(); + /** + Gives method bitmap should be read to implement a shader. + Also determines number and interpretation of "extra" parameters returned + by asABitmap + */ + enum BitmapType { + kNone_BitmapType, //<! Shader is not represented as a bitmap + kDefault_BitmapType,//<! Access bitmap using local coords transformed + // by matrix. No extras + kRadial_BitmapType, //<! Access bitmap by transforming local coordinates + // by the matrix and taking the distance of result + // from (0,0) as bitmap column. Bitmap is 1 pixel + // tall. No extras + kSweep_BitmapType, //<! Access bitmap by transforming local coordinates + // by the matrix and taking the angle of result + // to (0,0) as bitmap x coord, where angle = 0 is + // bitmap left edge of bitmap = 2pi is the + // right edge. Bitmap is 1 pixel tall. No extras + kTwoPointRadial_BitmapType + //<! Matrix transforms to space where (0,0) is + // the center of the starting circle. The second + // circle will be centered (x, 0) where x may be + // 0. The post-matrix space is normalized such + // that 1 is the second radius - first radius. + // Three extra parameters are returned: + // 0: x-offset of second circle center + // to first. + // 1: radius of first circle in post-matrix + // space + // 2: the second radius minus the first radius + // in pre-transformed space. + + }; /** Optional methods for shaders that can pretend to be a bitmap/texture - to play along with opengl. Default just returns false and ignores - the out parameters. + to play along with opengl. Default just returns kNone_BitmapType and + ignores the out parameters. + + @param outTexture if non-NULL will be the bitmap representing the shader + after return. + @param outMatrix if non-NULL will be the matrix to apply to vertices + to access the bitmap after return. + @param xy if non-NULL will be the tile modes that should be + used to access the bitmap after return. + @param twoPointRadialParams Two extra return values needed for two point + radial bitmaps. The first is the x-offset of + the second point and the second is the radius + about the first point. */ - virtual bool asABitmap(SkBitmap* outTexture, SkMatrix* outMatrix, - TileMode xy[2]); + virtual BitmapType asABitmap(SkBitmap* outTexture, SkMatrix* outMatrix, + TileMode xy[2], SkScalar* twoPointRadialParams); ////////////////////////////////////////////////////////////////////////// // Factory methods for stock shaders diff --git a/include/core/SkSize.h b/include/core/SkSize.h index 9df9508..8371c9b 100644 --- a/include/core/SkSize.h +++ b/include/core/SkSize.h @@ -83,19 +83,19 @@ struct SkSize : public SkTSize<SkScalar> { return *this; } - SkISize round() const { + SkISize toRound() const { SkISize s; s.set(SkScalarRound(fWidth), SkScalarRound(fHeight)); return s; } - - SkISize ceil() const { + + SkISize toCeil() const { SkISize s; s.set(SkScalarCeil(fWidth), SkScalarCeil(fHeight)); return s; } - SkISize floor() const { + SkISize toFloor() const { SkISize s; s.set(SkScalarFloor(fWidth), SkScalarFloor(fHeight)); return s; diff --git a/include/core/SkStream.h b/include/core/SkStream.h index 798c6be..b02d482 100644 --- a/include/core/SkStream.h +++ b/include/core/SkStream.h @@ -91,6 +91,7 @@ public: bool writeText(const char text[]); bool writeDecAsText(int32_t); + bool writeBigDecAsText(int64_t, int minDigits = 0); bool writeHexAsText(uint32_t, int minDigits = 0); bool writeScalarAsText(SkScalar); @@ -176,7 +177,11 @@ public: */ virtual void setMemory(const void* data, size_t length, bool copyData = false); - virtual void setMemoryOwned(const void* src, size_t size); + /** Replace any memory buffer with the specified buffer. The caller + must have allocated data with sk_malloc or sk_realloc, since it + will be freed with sk_free. + */ + void setMemoryOwned(const void* data, size_t length); void skipToAlign4(); virtual bool rewind(); virtual size_t read(void* buffer, size_t size); @@ -273,7 +278,7 @@ public: // modifies stream and returns true if offset + size is less than or equal to getOffset() bool write(const void* buffer, size_t offset, size_t size); bool read(void* buffer, size_t offset, size_t size); - size_t getOffset() { return fBytesWritten; } + size_t getOffset() const { return fBytesWritten; } // copy what has been written to the stream into dst void copyTo(void* dst) const; @@ -309,4 +314,3 @@ public: typedef SkFILEStream SkURLStream; #endif - diff --git a/include/core/SkString.h b/include/core/SkString.h index 5ecfb1e..4498bba 100644 --- a/include/core/SkString.h +++ b/include/core/SkString.h @@ -28,6 +28,8 @@ int SkStrStartsWithOneOf(const char string[], const char prefixes[]); #define SkStrAppendS32_MaxSize 11 char* SkStrAppendS32(char buffer[], int32_t); +#define SkStrAppendS64_MaxSize 20 +char* SkStrAppendS64(char buffer[], int64_t, int minDigits); #define SkStrAppendScalar_MaxSize 11 char* SkStrAppendScalar(char buffer[], SkScalar); @@ -93,6 +95,7 @@ public: void insert(size_t offset, const char text[], size_t len); void insertUnichar(size_t offset, SkUnichar); void insertS32(size_t offset, int32_t value); + void insertS64(size_t offset, int64_t value, int minDigits = 0); void insertHex(size_t offset, uint32_t value, int minDigits = 0); void insertScalar(size_t offset, SkScalar); @@ -101,6 +104,7 @@ public: void append(const char text[], size_t len) { this->insert((size_t)-1, text, len); } void appendUnichar(SkUnichar uni) { this->insertUnichar((size_t)-1, uni); } void appendS32(int32_t value) { this->insertS32((size_t)-1, value); } + void appendS64(int64_t value, int minDigits = 0) { this->insertS64((size_t)-1, value, minDigits); } void appendHex(uint32_t value, int minDigits = 0) { this->insertHex((size_t)-1, value, minDigits); } void appendScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } @@ -109,6 +113,7 @@ public: void prepend(const char text[], size_t len) { this->insert(0, text, len); } void prependUnichar(SkUnichar uni) { this->insertUnichar(0, uni); } void prependS32(int32_t value) { this->insertS32(0, value); } + void prependS64(int32_t value, int minDigits = 0) { this->insertS64(0, value, minDigits); } void prependHex(uint32_t value, int minDigits = 0) { this->insertHex(0, value, minDigits); } void prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); } @@ -165,4 +170,3 @@ private: }; #endif - diff --git a/include/core/SkTScopedPtr.h b/include/core/SkTScopedPtr.h new file mode 100644 index 0000000..1e5d4c4 --- /dev/null +++ b/include/core/SkTScopedPtr.h @@ -0,0 +1,83 @@ +/* + * 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 SkTScopedPtr_DEFINED +#define SkTScopedPtr_DEFINED + +#include "SkTypes.h" + +/** \class SkTScopedPtr + A SkTScopedPtr<T> is like a T*, except that the destructor of SkTScopedPtr<T> + automatically deletes the pointer it holds (if any). That is, SkTScopedPtr<T> + owns the T object that it points to. Like a T*, a SkTScopedPtr<T> may hold + either NULL or a pointer to a T object. Also like T*, SkTScopedPtr<T> is + thread-compatible, and once you dereference it, you get the threadsafety + guarantees of T. + + The size of a SkTScopedPtr is small: sizeof(SkTScopedPtr<T>) == sizeof(T*) +*/ +template <typename T> class SkTScopedPtr : SkNoncopyable { +public: + explicit SkTScopedPtr(T* o = NULL) : fObj(o) {} + ~SkTScopedPtr() { + enum { kTypeMustBeComplete = sizeof(T) }; + delete fObj; + } + + /** Delete the current object, if any. Then take ownership of the + passed object. + */ + void reset(T* o = NULL) { + if (o != fObj) { + enum { kTypeMustBeComplete = sizeof(T) }; + delete fObj; + fObj = o; + } + } + + /** Without deleting the current object, return it and forget about it. + Similar to calling get() and reset(), but the object is not deleted. + */ + T* release() { + T* retVal = fObj; + fObj = NULL; + return retVal; + } + + T& operator*() const { + SkASSERT(fObj != NULL); + return *fObj; + } + T* operator->() const { + SkASSERT(fObj != NULL); + return fObj; + } + T* get() const { return fObj; } + + bool operator==(T* o) const { return fObj == o; } + bool operator!=(T* o) const { return fObj != o; } + +private: + T* fObj; + + // Forbid comparison of SkTScopedPtr types. If T2 != T, it doesn't make + // sense, and if T2 == T, it still doesn't make sense because the same + // object can't be owned by two different scoped_ptrs. + template <class T2> bool operator==(SkTScopedPtr<T2> const& o2) const; + template <class T2> bool operator!=(SkTScopedPtr<T2> const& o2) const; +}; + +#endif diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h index d484389..55109bf 100644 --- a/include/core/SkTemplates.h +++ b/include/core/SkTemplates.h @@ -59,12 +59,13 @@ private: T* fObj; }; +// See also SkTScopedPtr. template <typename T> class SkAutoTDelete : SkNoncopyable { public: SkAutoTDelete(T* obj, bool deleteWhenDone = true) : fObj(obj) { - this->deleteWhenDone = deleteWhenDone; + fDeleteWhenDone = deleteWhenDone; } - ~SkAutoTDelete() { if (deleteWhenDone) delete fObj; } + ~SkAutoTDelete() { if (fDeleteWhenDone) delete fObj; } T* get() const { return fObj; } void free() { delete fObj; fObj = NULL; } @@ -72,7 +73,7 @@ public: private: T* fObj; - bool deleteWhenDone; + bool fDeleteWhenDone; }; template <typename T> class SkAutoTDeleteArray : SkNoncopyable { diff --git a/include/core/SkThread_platform.h b/include/core/SkThread_platform.h index 825b737..c6fd058 100644 --- a/include/core/SkThread_platform.h +++ b/include/core/SkThread_platform.h @@ -17,7 +17,7 @@ #ifndef SkThread_platform_DEFINED #define SkThread_platform_DEFINED -#ifdef ANDROID +#if defined(ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) #include <utils/threads.h> #include <utils/Atomic.h> @@ -61,7 +61,7 @@ public: private: bool fIsGlobal; enum { - kStorageIntCount = 12 + kStorageIntCount = 64 }; uint32_t fStorage[kStorageIntCount]; }; diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h index bf783a3..cea6ab1 100644 --- a/include/core/SkTypeface.h +++ b/include/core/SkTypeface.h @@ -20,6 +20,7 @@ #include "SkRefCnt.h" class SkStream; +class SkAdvancedTypefaceMetrics; class SkWStream; /** \class SkTypeface @@ -83,6 +84,18 @@ public: */ static SkTypeface* CreateFromName(const char familyName[], Style style); + /** Return a new reference to the typeface that covers a set of Unicode + code points with the specified Style. Use this call if you want to + pick any font that covers a given string of text. + + @param data UTF-16 characters + @param bytelength length of data, in bytes + @return reference to the closest-matching typeface. Call must call + unref() when they are done. + */ + static SkTypeface* CreateForChars(const void* data, size_t bytelength, + Style s); + /** Return a new reference to the typeface that most closely matches the requested typeface and specified Style. Use this call if you want to pick a new style from the same family of the existing typeface. @@ -118,6 +131,14 @@ public: */ static SkTypeface* Deserialize(SkStream*); + /** Retrieve detailed typeface metrics. Used by the PDF backend. + @param perGlyphInfo Indicate if the glyph specific information (advances + and names) should be populated. + @return The returned object has already been referenced. + */ + SkAdvancedTypefaceMetrics* getAdvancedTypefaceMetrics( + bool perGlyphInfo) const; + protected: /** uniqueID must be unique (please!) and non-zero */ diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h index 1f8ecaa..8dbd51b 100644 --- a/include/core/SkTypes.h +++ b/include/core/SkTypes.h @@ -103,27 +103,53 @@ static inline void sk_bzero(void* buffer, size_t size) { #define SkAssertResult(cond) cond #endif +namespace { + +template <bool> +struct SkCompileAssert { +}; + +} // namespace + +#define SK_COMPILE_ASSERT(expr, msg) \ + typedef SkCompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + /////////////////////////////////////////////////////////////////////// -/** Fast type for signed 8 bits. Use for parameter passing and local variables, not for storage -*/ -typedef int S8CPU; -/** Fast type for unsigned 8 bits. Use for parameter passing and local variables, not for storage -*/ -typedef int S16CPU; -/** Fast type for signed 16 bits. Use for parameter passing and local variables, not for storage -*/ -typedef unsigned U8CPU; -/** Fast type for unsigned 16 bits. Use for parameter passing and local variables, not for storage -*/ -typedef unsigned U16CPU; +/** + * Fast type for signed 8 bits. Use for parameter passing and local variables, + * not for storage. + */ +typedef int S8CPU; -/** Meant to be faster than bool (doesn't promise to be 0 or 1, just 0 or non-zero -*/ -typedef int SkBool; -/** Meant to be a small version of bool, for storage purposes. Will be 0 or 1 -*/ -typedef uint8_t SkBool8; +/** + * Fast type for unsigned 8 bits. Use for parameter passing and local + * variables, not for storage + */ +typedef unsigned U8CPU; + +/** + * Fast type for signed 16 bits. Use for parameter passing and local variables, + * not for storage + */ +typedef int S16CPU; + +/** + * Fast type for unsigned 16 bits. Use for parameter passing and local + * variables, not for storage + */ +typedef unsigned U16CPU; + +/** + * Meant to be faster than bool (doesn't promise to be 0 or 1, + * just 0 or non-zero + */ +typedef int SkBool; + +/** + * Meant to be a small version of bool, for storage purposes. Will be 0 or 1 + */ +typedef uint8_t SkBool8; #ifdef SK_DEBUG int8_t SkToS8(long); @@ -290,6 +316,20 @@ static inline uint32_t SkSetClearMask(uint32_t bits, bool cond, return cond ? bits | mask : bits & ~mask; } +/////////////////////////////////////////////////////////////////////////////// + +/** + * Use to cast a pointer to a different type, and maintaining strict-aliasing + */ +template <typename Dst> Dst SkTCast(const void* ptr) { + union { + const void* src; + Dst dst; + } data; + data.src = ptr; + return data.dst; +} + ////////////////////////////////////////////////////////////////////////////// /** \class SkNoncopyable @@ -300,7 +340,7 @@ be copied. It hides its copy-constructor and its assignment-operator. class SkNoncopyable { public: SkNoncopyable() {} - + private: SkNoncopyable(const SkNoncopyable&); SkNoncopyable& operator=(const SkNoncopyable&); @@ -311,7 +351,7 @@ public: SkAutoFree() : fPtr(NULL) {} explicit SkAutoFree(void* ptr) : fPtr(ptr) {} ~SkAutoFree() { sk_free(fPtr); } - + /** Return the currently allocate buffer, or null */ void* get() const { return fPtr; } @@ -325,7 +365,7 @@ public: fPtr = ptr; return prev; } - + /** Transfer ownership of the current ptr to the caller, setting the internal reference to null. Note the caller is reponsible for calling sk_free on the returned address. diff --git a/include/core/SkUtils.h b/include/core/SkUtils.h index 0700aeb..7cb2066 100644 --- a/include/core/SkUtils.h +++ b/include/core/SkUtils.h @@ -39,7 +39,7 @@ void sk_memset32_portable(uint32_t dst[], uint32_t value, int count); typedef void (*SkMemset32Proc)(uint32_t dst[], uint32_t value, int count); SkMemset32Proc SkMemset32GetPlatformProc(); -#ifdef ANDROID +#if defined(ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) #include "cutils/memory.h" #define sk_memset16(dst, value, count) android_memset16(dst, value, (count) << 1) diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h index d92d346..714468c 100644 --- a/include/core/SkXfermode.h +++ b/include/core/SkXfermode.h @@ -115,6 +115,13 @@ public: kLastMode = kExclusion_Mode }; + /** If the xfermode is one of the modes in the Mode enum, then asMode() + returns true and sets (if not null) mode accordingly. + This is a better version of IsMode(), which is only able to report + about modes that are expressible as coefficients. + */ + virtual bool asMode(Mode* mode); + /** Return an SkXfermode object for the specified mode. */ static SkXfermode* Create(Mode mode); @@ -137,7 +144,7 @@ public: return false and ignore the mode parameter. */ static bool IsMode(SkXfermode*, Mode* mode); - + Mode fMode; protected: @@ -184,6 +191,7 @@ public: // overrides from SkFlattenable virtual Factory getFactory() { return CreateProc; } virtual void flatten(SkFlattenableWriteBuffer&); + virtual bool asMode(SkXfermode::Mode* mode); protected: SkProcXfermode(SkFlattenableReadBuffer&); diff --git a/include/effects/SkBlurDrawLooper.h b/include/effects/SkBlurDrawLooper.h index 028b2eb..6f96ff6 100644 --- a/include/effects/SkBlurDrawLooper.h +++ b/include/effects/SkBlurDrawLooper.h @@ -29,7 +29,19 @@ class SkMaskFilter; */ class SkBlurDrawLooper : public SkDrawLooper { public: - SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color); + enum BlurFlags { + kNone_BlurFlag = 0x00, + /** + The blur layer's dx/dy/radius aren't affected by the canvas + transform. + */ + kIgnoreTransform_BlurFlag = 0x01, + /** mask for all blur flags */ + kAll_BlurFlag = 0x01 + }; + + SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color, + uint32_t flags = kNone_BlurFlag); virtual ~SkBlurDrawLooper(); // overrides from SkDrawLooper @@ -41,6 +53,7 @@ public: return SkNEW_ARGS(SkBlurDrawLooper, (buffer)); } + protected: SkBlurDrawLooper(SkFlattenableReadBuffer&); // overrides from SkFlattenable @@ -55,6 +68,7 @@ private: SkColor fBlurColor; SkColor fSavedColor; // remember the original int fSaveCount; + uint32_t fBlurFlags; enum State { kBeforeEdge, diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h index b90ea5e..0a68f54 100644 --- a/include/effects/SkBlurMaskFilter.h +++ b/include/effects/SkBlurMaskFilter.h @@ -32,12 +32,22 @@ public: kBlurStyleCount }; + enum BlurFlags { + kNone_BlurFlag = 0x00, + /** The blur layer's radius is not affected by transforms */ + kIgnoreTransform_BlurFlag = 0x01, + /** mask for all blur flags */ + kAll_BlurFlag = 0x01 + }; + /** Create a blur maskfilter. @param radius The radius to extend the blur from the original mask. Must be > 0. @param style The BlurStyle to use + @param flags Flags to use - defaults to none @return The new blur maskfilter */ - static SkMaskFilter* Create(SkScalar radius, BlurStyle style); + static SkMaskFilter* Create(SkScalar radius, BlurStyle style, + uint32_t flags = kNone_BlurFlag); /** Create an emboss maskfilter @param direction array of 3 scalars [x, y, z] specifying the direction of the light source diff --git a/include/effects/SkGroupShape.h b/include/effects/SkGroupShape.h new file mode 100644 index 0000000..2c851fa --- /dev/null +++ b/include/effects/SkGroupShape.h @@ -0,0 +1,150 @@ +#ifndef SkGroupShape_DEFINED +#define SkGroupShape_DEFINED + +#include "SkMatrix.h" +#include "SkShape.h" +#include "SkTDArray.h" +#include "SkThread.h" + +template <typename T> class SkTRefCnt : public T { +public: + SkTRefCnt() : fRefCnt(1) {} + ~SkTRefCnt() { SkASSERT(1 == fRefCnt); } + + int32_t getRefCnt() const { return fRefCnt; } + + /** Increment the reference count. Must be balanced by a call to unref(). + */ + void ref() const { + SkASSERT(fRefCnt > 0); + sk_atomic_inc(&fRefCnt); + } + + /** Decrement the reference count. If the reference count is 1 before the + decrement, then call delete on the object. Note that if this is the + case, then the object needs to have been allocated via new, and not on + the stack. + */ + void unref() const { + SkASSERT(fRefCnt > 0); + if (sk_atomic_dec(&fRefCnt) == 1) { + fRefCnt = 1; // so our destructor won't complain + SkDELETE(this); + } + } + + static void SafeRef(const SkTRefCnt* obj) { + if (obj) { + obj->ref(); + } + } + + static void SafeUnref(const SkTRefCnt* obj) { + if (obj) { + obj->unref(); + } + } + +private: + mutable int32_t fRefCnt; +}; + +class SkMatrixRef : public SkTRefCnt<SkMatrix> { +public: + SkMatrixRef() { this->reset(); } + explicit SkMatrixRef(const SkMatrix& matrix) { + SkMatrix& m = *this; + m = matrix; + } + + SkMatrix& operator=(const SkMatrix& matrix) { + SkMatrix& m = *this; + m = matrix; + return m; + } +}; + +class SkGroupShape : public SkShape { +public: + SkGroupShape(); + virtual ~SkGroupShape(); + + /** Return the number of child shapes in this group + */ + int countShapes() const; + + /** Return the shape at the specified index. Note this does not affect the + owner count of the index'd shape. If index is out of range, returns NULL + */ + SkShape* getShape(int index, SkMatrixRef** = NULL) const; + + /** Helper function to return the matrixref of the specified shape. + */ + SkMatrixRef* getShapeMatrixRef(int index) const { + SkMatrixRef* mr = NULL; + (void)this->getShape(index, &mr); + return mr; + } + + /** Ref the specified shape, and insert it into the child list at the + specified index. If index == countShapes(), then the shape will be + appended to the child list, otherwise if index is out of range, the + shape is not added. Either way, the shape parameter is returned. + + Child shapes are drawn in order, after the parent, so the shape at index + 0 will be drawn first, and the shape at index countShapes() - 1 will be + drawn last. + */ + void addShape(int index, SkShape*, SkMatrixRef* = NULL); + + void addShape(int index, SkShape* shape, const SkMatrix& matrix) { + SkMatrixRef* mr = SkNEW_ARGS(SkMatrixRef, (matrix)); + this->addShape(index, shape, mr); + mr->unref(); + } + + /** Helper method to append a shape, passing countShapes() for the index + */ + SkShape* appendShape(SkShape* shape, SkMatrixRef* mr = NULL) { + this->addShape(this->countShapes(), shape, mr); + return shape; + } + + SkShape* appendShape(SkShape* shape, const SkMatrix& matrix) { + this->addShape(this->countShapes(), shape, matrix); + return shape; + } + + /** Unref the specified index, and remove it from the child list. If index + is out of range, does nothing. + */ + void removeShape(int index); + + /** Unrefs and removes all of the child shapes + */ + void removeAllShapes(); + + // overrides + virtual Factory getFactory(); + virtual void flatten(SkFlattenableWriteBuffer&); + + // public for Registrar + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + +protected: + // overrides + virtual void onDraw(SkCanvas*); + + SkGroupShape(SkFlattenableReadBuffer&); + +private: + struct Rec { + SkShape* fShape; + SkMatrixRef* fMatrixRef; + }; + SkTDArray<Rec> fList; + + typedef SkShape INHERITED; +}; + +#endif diff --git a/include/effects/SkRectShape.h b/include/effects/SkRectShape.h new file mode 100644 index 0000000..9b8cfc1 --- /dev/null +++ b/include/effects/SkRectShape.h @@ -0,0 +1,56 @@ +#ifndef SkRectShape_DEFINED +#define SkRectShape_DEFINED + +#include "SkShape.h" +#include "SkPaint.h" +#include "SkSize.h" + +class SkPaintShape : public SkShape { +public: + SkPaintShape(); + + SkPaint& paint() { return fPaint; } + const SkPaint& paint() const { return fPaint; } + + // overrides + virtual void flatten(SkFlattenableWriteBuffer&); + +protected: + SkPaintShape(SkFlattenableReadBuffer& buffer); + +private: + SkPaint fPaint; + + typedef SkShape INHERITED; +}; + +class SkRectShape : public SkPaintShape { +public: + SkRectShape(); + + void setRect(const SkRect&); + void setOval(const SkRect&); + void setCircle(SkScalar x, SkScalar y, SkScalar radius); + void setRRect(const SkRect&, SkScalar rx, SkScalar ry); + + // overrides + virtual Factory getFactory(); + virtual void flatten(SkFlattenableWriteBuffer&); + + // public for Registrar + static SkFlattenable* CreateProc(SkFlattenableReadBuffer&); + +protected: + SkRectShape(SkFlattenableReadBuffer&); + + // overrides + virtual void onDraw(SkCanvas*); + +private: + SkRect fBounds; + SkSize fRadii; + + typedef SkPaintShape INHERITED; +}; + +#endif diff --git a/include/images/SkImageRef.h b/include/images/SkImageRef.h index 8672b25..800f12e 100644 --- a/include/images/SkImageRef.h +++ b/include/images/SkImageRef.h @@ -59,7 +59,7 @@ public: /** Return true if the image can be decoded and is opaque. Calling this method will decode and set the pixels in the specified bitmap and - set the opaque flag. + sets the isOpaque flag. */ bool isOpaque(SkBitmap* bm); diff --git a/include/ports/SkHarfBuzzFont.h b/include/ports/SkHarfBuzzFont.h new file mode 100644 index 0000000..b1fce0e --- /dev/null +++ b/include/ports/SkHarfBuzzFont.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SkHarfBuzzFont_DEFINED +#define SkHarfBuzzFont_DEFINED + +extern "C" { +#include "harfbuzz-shaper.h" +//#include "harfbuzz-unicode.h" +} + +#include "SkTypes.h" + +class SkPaint; +class SkTypeface; + +class SkHarfBuzzFont { +public: + /** The subclass returns the typeface for this font, or NULL + */ + virtual SkTypeface* getTypeface() const = 0; + /** The subclass sets the text related attributes of the paint. + e.g. textSize, typeface, textSkewX, etc. + All of the attributes that could effect how the text is measured. + Color information (e.g. color, xfermode, shader, etc.) are not required. + */ + virtual void setupPaint(SkPaint*) const = 0; + + /** Implementation of HB_GetFontTableFunc, using SkHarfBuzzFont* as + the first parameter. + */ + static HB_Error GetFontTableFunc(void* skharfbuzzfont, const HB_Tag tag, + HB_Byte* buffer, HB_UInt* len); + + static const HB_FontClass& GetFontClass(); +}; + +#endif + diff --git a/include/svg/SkSVGParser.h b/include/svg/SkSVGParser.h index 86ae4c2..83b120d 100644 --- a/include/svg/SkSVGParser.h +++ b/include/svg/SkSVGParser.h @@ -32,7 +32,7 @@ class SkSVGElement; class SkSVGParser : public SkXMLParser { public: - SkSVGParser(); + SkSVGParser(SkXMLParserError* err = NULL); virtual ~SkSVGParser(); void _addAttribute(const char* attrName, const char* attrValue) { fXMLWriter.addAttribute(attrName, attrValue); } diff --git a/include/svg/SkSVGTypes.h b/include/svg/SkSVGTypes.h index 38d63ce..99d8ca1 100644 --- a/include/svg/SkSVGTypes.h +++ b/include/svg/SkSVGTypes.h @@ -40,6 +40,7 @@ enum SkSVGTypes { SkSVGType_Symbol, SkSVGType_Text, SkSVGType_Tspan, + SkSVGType_Unknown, SkSVGType_Use }; diff --git a/include/text/SkTextLayout.h b/include/text/SkTextLayout.h new file mode 100644 index 0000000..2152307 --- /dev/null +++ b/include/text/SkTextLayout.h @@ -0,0 +1,50 @@ +#ifndef SkTextLayout_DEFINED +#define SkTextLayout_DEFINED + +#include "SkPaint.h" +#include "SkRefCnt.h" + +class SkTextStyle : public SkRefCnt { +public: + SkTextStyle(); + SkTextStyle(const SkTextStyle&); + explicit SkTextStyle(const SkPaint&); + virtual ~SkTextStyle(); + + const SkPaint& paint() const { return fPaint; } + SkPaint& paint() { return fPaint; } + + // todo: bidi-override, language + +private: + SkPaint fPaint; +}; + +class SkTextLayout { +public: + SkTextLayout(); + ~SkTextLayout(); + + void setText(const char text[], size_t length); + void setBounds(const SkRect& bounds); + + SkTextStyle* getDefaultStyle() const { return fDefaultStyle; } + SkTextStyle* setDefaultStyle(SkTextStyle*); + +// SkTextStyle* setStyle(SkTextStyle*, size_t offset, size_t length); + + void draw(SkCanvas* canvas); + +private: + SkTDArray<char> fText; + SkTextStyle* fDefaultStyle; + SkRect fBounds; + + // cache + struct Line; + struct GlyphRun; + SkTDArray<Line*> fLines; +}; + +#endif + diff --git a/include/utils/SkCubicInterval.h b/include/utils/SkCubicInterval.h new file mode 100644 index 0000000..bd6fc5f --- /dev/null +++ b/include/utils/SkCubicInterval.h @@ -0,0 +1,16 @@ +#ifndef SkCubicInterval_DEFINED +#define SkCubicInterval_DEFINED + +#include "SkPoint.h" + +SkScalar SkEvalCubicInterval(SkScalar x1, SkScalar y1, + SkScalar x2, SkScalar y2, + SkScalar unitX); + +static inline SkScalar SkEvalCubicInterval(const SkPoint pts[2], SkScalar x) { + return SkEvalCubicInterval(pts[0].fX, pts[0].fY, + pts[1].fX, pts[1].fY, x); +} + +#endif + diff --git a/include/utils/SkEGLContext.h b/include/utils/SkEGLContext.h new file mode 100644 index 0000000..4b17be1 --- /dev/null +++ b/include/utils/SkEGLContext.h @@ -0,0 +1,20 @@ +#ifndef SkEGLContext_DEFINED +#define SkEGLContext_DEFINED + +#include "SkTypes.h" + +/** + * Create an offscreen opengl context + */ +class SkEGLContext { +public: + SkEGLContext(); + ~SkEGLContext(); + + bool init(int width, int height); + +private: + void* fContext; +}; + +#endif diff --git a/include/utils/SkLayer.h b/include/utils/SkLayer.h index c42261f..80a2137 100644 --- a/include/utils/SkLayer.h +++ b/include/utils/SkLayer.h @@ -128,6 +128,8 @@ private: uint32_t fFlags; SkTDArray<SkLayer*> m_children; + + typedef SkRefCnt INHERITED; }; #endif diff --git a/include/views/SkOSSound.h b/include/utils/SkParsePath.h index 5d77955..d271f7e 100644 --- a/include/views/SkOSSound.h +++ b/include/utils/SkParsePath.h @@ -14,22 +14,17 @@ * limitations under the License. */ -#ifndef SkOSSound_DEFINED -#define SkOSSound_DEFINED +#ifndef SkParsePath_DEFINED +#define SkParsePath_DEFINED -#include "SkTypes.h" +#include "SkPath.h" -class SkOSSound { -public: - static void Play(const char path[]); - static void Pause(); - static void Resume(); - static bool TogglePause(); // returns true if we are now playing, or false if we're now paused - static void Stop(); +class SkString; - // volume runs from 0 (silent) to 0xFF (max-volume) - static uint8_t GetVolume(); - static void SetVolume(U8CPU volume); +class SkParsePath { +public: + static bool FromSVGString(const char str[], SkPath*); + static void ToSVGString(const SkPath&, SkString*); }; #endif diff --git a/include/utils/SkProxyCanvas.h b/include/utils/SkProxyCanvas.h index 082e0fd..98ef778 100644 --- a/include/utils/SkProxyCanvas.h +++ b/include/utils/SkProxyCanvas.h @@ -22,10 +22,7 @@ public: // overrides from SkCanvas virtual bool getViewport(SkIPoint* size) const; - virtual bool setViewport(int x, int y); - virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap); - virtual int save(SaveFlags flags = kMatrixClip_SaveFlag); virtual int saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags = kARGB_ClipLayer_SaveFlag); diff --git a/include/utils/SkTextBox.h b/include/utils/SkTextBox.h index 2c34448..3471f5b 100644 --- a/include/utils/SkTextBox.h +++ b/include/utils/SkTextBox.h @@ -62,10 +62,18 @@ public: void draw(SkCanvas*, const char text[], size_t len, const SkPaint&); + void setText(const char text[], size_t len, const SkPaint&); + void draw(SkCanvas*); + int countLines() const; + SkScalar getTextHeight() const; + private: SkRect fBox; SkScalar fSpacingMul, fSpacingAdd; uint8_t fMode, fSpacingAlign; + const char* fText; + size_t fLen; + const SkPaint* fPaint; }; class SkTextLineBreaker { diff --git a/include/utils/mac/SkCGUtils.h b/include/utils/mac/SkCGUtils.h index 3b74b55..db67edf 100644 --- a/include/utils/mac/SkCGUtils.h +++ b/include/utils/mac/SkCGUtils.h @@ -1,10 +1,37 @@ #ifndef SkCGUtils_DEFINED #define SkCGUtils_DEFINED -#include <Carbon/Carbon.h> +#include "SkTypes.h" + +#ifdef SK_BUILD_FOR_MAC + #include <Carbon/Carbon.h> +#else + #include <CoreGraphics/CoreGraphics.h> +#endif class SkBitmap; -CGImageRef SkCreateCGImageRef(const SkBitmap&); +/** + * Create an imageref from the specified bitmap using the specified colorspace. + * If space is NULL, then CGColorSpaceCreateDeviceRGB() is used. + */ +CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm, + CGColorSpaceRef space); + +/** + * Create an imageref from the specified bitmap using the colorspace returned + * by CGColorSpaceCreateDeviceRGB() + */ +static inline CGImageRef SkCreateCGImageRef(const SkBitmap& bm) { + return SkCreateCGImageRefWithColorspace(bm, NULL); +} + +/** + * Draw the bitmap into the specified CG context. The bitmap will be converted + * to a CGImage using the generic RGB colorspace. (x,y) specifies the position + * of the top-left corner of the bitmap. The bitmap is converted using the + * colorspace returned by CGColorSpaceCreateDeviceRGB() + */ +void SkCGDrawBitmap(CGContextRef, const SkBitmap&, float x, float y); #endif diff --git a/include/views/SkEvent.h b/include/views/SkEvent.h index 2b43f34..f6719d6 100644 --- a/include/views/SkEvent.h +++ b/include/views/SkEvent.h @@ -21,8 +21,6 @@ #include "SkMetaData.h" #include "SkString.h" -//class SkOSWindow; - /** 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, @@ -100,6 +98,9 @@ public: */ 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); } @@ -110,6 +111,9 @@ public: /** 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); } @@ -124,6 +128,9 @@ public: /** 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; } @@ -197,6 +204,12 @@ public: */ 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 **/ //////////////////////////////////////////////////// diff --git a/include/views/SkImageView.h b/include/views/SkImageView.h index 157abb1..57215c9 100644 --- a/include/views/SkImageView.h +++ b/include/views/SkImageView.h @@ -22,7 +22,7 @@ class SkAnimator; class SkBitmap; -struct SkMatrix; +class SkMatrix; class SkImageView : public SkView { public: diff --git a/include/views/SkMetaData.h b/include/views/SkMetaData.h index 21739fe..9509710 100644 --- a/include/views/SkMetaData.h +++ b/include/views/SkMetaData.h @@ -35,6 +35,7 @@ public: const char* findString(const char name[]) const; bool findPtr(const char name[], void** value = NULL) const; bool findBool(const char name[], bool* value = NULL) const; + const void* findData(const char name[], size_t* byteCount = NULL) const; bool hasS32(const char name[], int32_t value) const { @@ -62,6 +63,11 @@ public: bool v; return this->findBool(name, &v) && v == value; } + bool hasData(const char name[], const void* data, size_t byteCount) const { + size_t len; + const void* ptr = this->findData(name, &len); + return NULL != ptr && len == byteCount && !memcmp(ptr, data, len); + } void setS32(const char name[], int32_t value); void setScalar(const char name[], SkScalar value); @@ -69,12 +75,15 @@ public: void setString(const char name[], const char value[]); void setPtr(const char name[], void* value); void setBool(const char name[], bool value); + // the data is copied from the input pointer. + void setData(const char name[], const void* data, size_t byteCount); bool removeS32(const char name[]); bool removeScalar(const char name[]); bool removeString(const char name[]); bool removePtr(const char name[]); bool removeBool(const char name[]); + bool removeData(const char name[]); SkDEBUGCODE(static void UnitTest();) @@ -84,6 +93,7 @@ public: kString_Type, kPtr_Type, kBool_Type, + kData_Type, kTypeCount }; diff --git a/include/views/SkOSWindow_Mac.h b/include/views/SkOSWindow_Mac.h index 98e76b0..3a26d5a 100644 --- a/include/views/SkOSWindow_Mac.h +++ b/include/views/SkOSWindow_Mac.h @@ -33,7 +33,12 @@ public: static OSStatus EventHandler(EventHandlerCallRef inHandler, EventRef inEvent, void* userData); - void doPaint(void* ctx); + void doPaint(void* ctx); + + + bool attachGL(const SkBitmap* offscreen); + void detachGL(); + void presentGL(); protected: // overrides from SkEventSink @@ -43,10 +48,12 @@ protected: // overrides from SkView virtual void onAddMenu(const SkOSMenu*); virtual void onSetTitle(const char[]); + private: void* fHWND; void* fHVIEW; + void* fAGLCtx; typedef SkWindow INHERITED; }; diff --git a/include/views/SkOSWindow_SDL.h b/include/views/SkOSWindow_SDL.h new file mode 100644 index 0000000..0ff24f3 --- /dev/null +++ b/include/views/SkOSWindow_SDL.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkOSWindow_SDL_DEFINED +#define SkOSWindow_SDL_DEFINED + +#include "SDL.h" +#include "SkWindow.h" + +class SkGLCanvas; + +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* screen); + virtual ~SkOSWindow(); + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + void handleSDLEvent(const SDL_Event& event); + +protected: + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + virtual void onSetTitle(const char[]); + +private: + SDL_Surface* fScreen; + SDL_Surface* fSurface; + SkGLCanvas* fGLCanvas; + + void doDraw(); + + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/include/views/SkOSWindow_Win.h b/include/views/SkOSWindow_Win.h index 0a70f75..09f0c5c 100644 --- a/include/views/SkOSWindow_Win.h +++ b/include/views/SkOSWindow_Win.h @@ -22,12 +22,23 @@ 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(const SkBitmap* offscreen); + 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); @@ -45,14 +56,23 @@ protected: // overrides from SkView virtual void onAddMenu(const SkOSMenu*); + virtual void onSetTitle(const char title[]); + private: - void* fHWND; + void* fHWND; + + void doPaint(void* ctx); + + void* fHGLRC; + + bool fGLAttached; - void doPaint(void* ctx); + void* fD3D9Device; + bool fD3D9Attached; - HMENU fMBar; + HMENU fMBar; - typedef SkWindow INHERITED; + typedef SkWindow INHERITED; }; #endif diff --git a/include/views/SkView.h b/include/views/SkView.h index fc36d34..d3633db 100644 --- a/include/views/SkView.h +++ b/include/views/SkView.h @@ -38,6 +38,7 @@ public: kFocusable_Shift, kFlexH_Shift, kFlexV_Shift, + kNoClip_Shift, kFlagShiftCount }; @@ -47,6 +48,7 @@ public: 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) }; @@ -66,10 +68,12 @@ public: 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; } @@ -302,7 +306,7 @@ protected: 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&); + 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) diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h index 1c8f9a3..5deefd5 100644 --- a/include/views/SkWindow.h +++ b/include/views/SkWindow.h @@ -19,6 +19,7 @@ #include "SkView.h" #include "SkBitmap.h" +#include "SkMatrix.h" #include "SkRegion.h" #include "SkEvent.h" #include "SkKey.h" @@ -29,6 +30,8 @@ #endif //#define USE_GX_SCREEN +class SkCanvas; + class SkOSMenu; class SkWindow : public SkView { @@ -44,7 +47,13 @@ public: void eraseRGB(U8CPU r, U8CPU g, U8CPU b); bool isDirty() const { return !fDirtyRgn.isEmpty(); } - bool update(SkIRect* updateArea); + 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); @@ -56,6 +65,11 @@ public: 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&); @@ -68,7 +82,7 @@ protected: virtual void onSetTitle(const char title[]) {} // overrides from SkView - virtual bool handleInval(const SkRect&); + virtual bool handleInval(const SkRect*); virtual bool onGetFocusView(SkView** focus) const; virtual bool onSetFocusView(SkView* focus); @@ -84,6 +98,7 @@ private: bool fWaitingOnInval; SkString fTitle; + SkMatrix fMatrix; typedef SkView INHERITED; }; @@ -100,6 +115,8 @@ private: #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/src/animator/SkAnimate3DSchema.xsd b/src/animator/SkAnimate3DSchema.xsd new file mode 100644 index 0000000..5063b75 --- /dev/null +++ b/src/animator/SkAnimate3DSchema.xsd @@ -0,0 +1,39 @@ +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:Sk="http://www.skia.com/schema/SkAnimateSchema.xsd"
+ targetNamespace="urn:skia3D" xmlns:Sk3D="urn:skia3D">
+
+ <xs:simpleType name="Patch" >
+ <xs:restriction base="xs:string" >
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="Point" >
+ <xs:restriction base="xs:string" >
+ <xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *[ ,] *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)){2}" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:element name="camera">
+ <xs:complexType >
+ <xs:attribute name="axis" type="Sk3D:Point" />
+ <xs:attribute name="hackHeight" type="Sk:Float" />
+ <xs:attribute name="hackWidth" type="Sk:Float" />
+ <xs:attribute name="location" type="Sk3D:Point" />
+ <xs:attribute name="observer" type="Sk3D:Point" />
+ <xs:attribute name="patch" type="Sk3D:Patch" />
+ <xs:attribute name="zenith" type="Sk3D:Point" />
+ <xs:attribute name="id" type="xs:ID" />
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="patch">
+ <xs:complexType >
+ <xs:attribute name="origin" type="Sk3D:Point" />
+ <xs:attribute name="rotateDegrees" type="Sk:MemberFunction" />
+ <xs:attribute name="u" type="Sk3D:Point" />
+ <xs:attribute name="v" type="Sk3D:Point" />
+ <xs:attribute name="id" type="xs:ID" />
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
diff --git a/src/animator/SkAnimate3DSchema.xsx b/src/animator/SkAnimate3DSchema.xsx new file mode 100644 index 0000000..ceb7d89 --- /dev/null +++ b/src/animator/SkAnimate3DSchema.xsx @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--This file is auto-generated by the XML Schema Designer. It holds layout information for components on the designer surface.-->
+<XSDDesignerLayout /> diff --git a/src/animator/SkAnimateSchema.xsd b/src/animator/SkAnimateSchema.xsd new file mode 100644 index 0000000..f7af332 --- /dev/null +++ b/src/animator/SkAnimateSchema.xsd @@ -0,0 +1,2787 @@ +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" +xmlns:Sk="urn:screenplay" targetNamespace="urn:screenplay"> + + <!-- /** Animate + An ID of an element of type <animate> or <set> + */ --> + <xs:simpleType name="Animate"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** 3D_Point + An array of three floats in ECMAScript notation: [x, y, z]. + */ --> + <xs:simpleType name="3D_Point"> + <xs:restriction base="xs:string"> + <xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *, *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)){2}" /> + </xs:restriction> + </xs:simpleType> + + <!-- /** ARGB + The red, green, blue, and optional alpha color components. + */ --> + <xs:simpleType name="ARGB"> + <xs:restriction base="xs:string"> + <!-- @pattern #[0-9a-fA-F]{3} #rgb contains three hexadecimal digits. #rgb is equivalent to 0xFFrrggbb. --> + <xs:pattern value="#[0-9a-fA-F]{3}"/> + <!-- @pattern #[0-9a-fA-F]{4} #argb contains four hexadecimal digits. #argb is equivalent to 0xaarrggbb. --> + <xs:pattern value="#[0-9a-fA-F]{4}"/> + <!-- @pattern #[0-9a-fA-F]{6} #rrggbb contains six hexadecimal digits. #rrggbb is equivalent to 0xFFrrggbb. --> + <xs:pattern value="#[0-9a-fA-F]{6}"/> + <!-- @pattern #[0-9a-fA-F]{8} #aarrggbb contains eight hexadecimal digits. #aarrggbb is equivalent to 0xaarrggbb. --> + <xs:pattern value="#[0-9a-fA-F]{8}"/> + <!-- @pattern 0[xX][0-9a-fA-F]{8} 0xaarrggbb describes the color as a packed hexadecimal; each pair of digits + corresponds to alpha, red, green, and blue respectively. --> + <xs:pattern value="0[xX][0-9a-fA-F]{8}"/> + <!-- @pattern rgb\(\d+{1,3},\d+{1,3},\d+{1,3}\) rgb(r, g, b) describes color with three integers ranging from 0 to 255, + corresponding to red, green, and blue respectively. --> + <xs:pattern value="rgb\(\d+{1,3},\d+{1,3},\d+{1,3}\)"/> + <!-- @patternList Color can be described by the following standard CSS color names. --> + <xs:pattern value="aliceblue"/> + <xs:pattern value="antiquewhite"/> + <xs:pattern value="aqua"/> + <xs:pattern value="aquamarine"/> + <xs:pattern value="azure"/> + <xs:pattern value="beige"/> + <xs:pattern value="bisque"/> + <xs:pattern value="black"/> + <xs:pattern value="blanchedalmond"/> + <xs:pattern value="blue"/> + <xs:pattern value="blueviolet"/> + <xs:pattern value="brown"/> + <xs:pattern value="burlywood"/> + <xs:pattern value="cadetblue"/> + <xs:pattern value="chartreuse"/> + <xs:pattern value="chocolate"/> + <xs:pattern value="coral"/> + <xs:pattern value="cornflowerblue"/> + <xs:pattern value="cornsilk"/> + <xs:pattern value="crimson"/> + <xs:pattern value="cyan"/> + <xs:pattern value="darkblue"/> + <xs:pattern value="darkcyan"/> + <xs:pattern value="darkgoldenrod"/> + <xs:pattern value="darkgray"/> + <xs:pattern value="darkgreen"/> + <xs:pattern value="darkkhaki"/> + <xs:pattern value="darkmagenta"/> + <xs:pattern value="darkolivegreen"/> + <xs:pattern value="darkorange"/> + <xs:pattern value="darkorchid"/> + <xs:pattern value="darkred"/> + <xs:pattern value="darksalmon"/> + <xs:pattern value="darkseagreen"/> + <xs:pattern value="darkslateblue"/> + <xs:pattern value="darkslategray"/> + <xs:pattern value="darkturquoise"/> + <xs:pattern value="darkviolet"/> + <xs:pattern value="deeppink"/> + <xs:pattern value="deepskyblue"/> + <xs:pattern value="dimgray"/> + <xs:pattern value="dodgerblue"/> + <xs:pattern value="firebrick"/> + <xs:pattern value="floralwhite"/> + <xs:pattern value="forestgreen"/> + <xs:pattern value="fuchsia"/> + <xs:pattern value="gainsboro"/> + <xs:pattern value="ghostwhite"/> + <xs:pattern value="gold"/> + <xs:pattern value="goldenrod"/> + <xs:pattern value="gray"/> + <xs:pattern value="green"/> + <xs:pattern value="greenyellow"/> + <xs:pattern value="honeydew"/> + <xs:pattern value="hotpink"/> + <xs:pattern value="indianred"/> + <xs:pattern value="indigo"/> + <xs:pattern value="ivory"/> + <xs:pattern value="khaki"/> + <xs:pattern value="lavender"/> + <xs:pattern value="lavenderblush"/> + <xs:pattern value="lawngreen"/> + <xs:pattern value="lemonchiffon"/> + <xs:pattern value="lightblue"/> + <xs:pattern value="lightcoral"/> + <xs:pattern value="lightcyan"/> + <xs:pattern value="lightgoldenrodyellow"/> + <xs:pattern value="lightgreen"/> + <xs:pattern value="lightgrey"/> + <xs:pattern value="lightpink"/> + <xs:pattern value="lightsalmon"/> + <xs:pattern value="lightseagreen"/> + <xs:pattern value="lightskyblue"/> + <xs:pattern value="lightslategray"/> + <xs:pattern value="lightsteelblue"/> + <xs:pattern value="lightyellow"/> + <xs:pattern value="lime"/> + <xs:pattern value="limegreen"/> + <xs:pattern value="linen"/> + <xs:pattern value="magenta"/> + <xs:pattern value="maroon"/> + <xs:pattern value="mediumaquamarine"/> + <xs:pattern value="mediumblue"/> + <xs:pattern value="mediumorchid"/> + <xs:pattern value="mediumpurple"/> + <xs:pattern value="mediumseagreen"/> + <xs:pattern value="mediumslateblue"/> + <xs:pattern value="mediumspringgreen"/> + <xs:pattern value="mediumturquoise"/> + <xs:pattern value="mediumvioletred"/> + <xs:pattern value="midnightblue"/> + <xs:pattern value="mintcream"/> + <xs:pattern value="mistyrose"/> + <xs:pattern value="moccasin"/> + <xs:pattern value="navajowhite"/> + <xs:pattern value="navy"/> + <xs:pattern value="oldlace"/> + <xs:pattern value="olive"/> + <xs:pattern value="olivedrab"/> + <xs:pattern value="orange"/> + <xs:pattern value="orangered"/> + <xs:pattern value="orchid"/> + <xs:pattern value="palegoldenrod"/> + <xs:pattern value="palegreen"/> + <xs:pattern value="paleturquoise"/> + <xs:pattern value="palevioletred"/> + <xs:pattern value="papayawhip"/> + <xs:pattern value="peachpuff"/> + <xs:pattern value="peru"/> + <xs:pattern value="pink"/> + <xs:pattern value="plum"/> + <xs:pattern value="powderblue"/> + <xs:pattern value="purple"/> + <xs:pattern value="red"/> + <xs:pattern value="rosybrown"/> + <xs:pattern value="royalblue"/> + <xs:pattern value="saddlebrown"/> + <xs:pattern value="salmon"/> + <xs:pattern value="sandybrown"/> + <xs:pattern value="seagreen"/> + <xs:pattern value="seashell"/> + <xs:pattern value="sienna"/> + <xs:pattern value="silver"/> + <xs:pattern value="skyblue"/> + <xs:pattern value="slateblue"/> + <xs:pattern value="slategray"/> + <xs:pattern value="snow"/> + <xs:pattern value="springgreen"/> + <xs:pattern value="steelblue"/> + <xs:pattern value="tan"/> + <xs:pattern value="teal"/> + <xs:pattern value="thistle"/> + <xs:pattern value="tomato"/> + <xs:pattern value="turquoise"/> + <xs:pattern value="violet"/> + <xs:pattern value="wheat"/> + <xs:pattern value="white"/> + <xs:pattern value="whitesmoke"/> + <xs:pattern value="yellow"/> + <!--@patternListLast --> + <xs:pattern value="yellowgreen"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** AddMode + AddMode controls how the add element adds its referenced element to the + display list. By default, the referenced element remains in the add element + so that the add element's use attribute may be animated to change the + element it refers to. Setting the mode attribute to "immediate" causes the + add element to put the referenced element in the display list directly. + The move and replace elements are not affected by the mode attribute; + they always move or replace the referenced element directly. + */ --> + <xs:simpleType name="AddMode"> + <xs:restriction base="xs:string"> + <!-- @pattern immediate Puts the referenced element in the display list. --> + <xs:pattern value="immediate"/> + <!-- @pattern indirect Puts the containing element in the display list. --> + <xs:pattern value="indirect"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Align + Align places text to the left, center, or right of the text position. + */ --> + <xs:simpleType name="Align"> + <xs:restriction base="xs:string"> + <!-- @pattern left The first character in the text string is drawn at the text position. --> + <xs:pattern value="left"/> + <!-- @pattern center The text string is measured and centered on the text position. --> + <xs:pattern value="center"/> + <!-- @pattern right The last character in the text string is drawn to the left of the text position. --> + <xs:pattern value="right"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** ApplyMode + ApplyMode affects how the apply element animates values. + */ --> + <xs:simpleType name="ApplyMode"> + <xs:restriction base="xs:string"> + <!-- @pattern immediate Iterates through all animation values immediately. --> + <xs:pattern value="immediate"/> + <!-- @pattern once Performs the animation at once without adding the scope to + the display list. --> + <xs:pattern value="once"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** ApplyTransition + ApplyTransition affects how the apply element sets the time of the animators. + */ --> + <xs:simpleType name="ApplyTransition"> + <xs:restriction base="xs:string"> + <!-- @pattern reverse Performs the animation in reverse. --> + <xs:pattern value="reverse"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Base64 + Base64 describes 8 bit binary using 64 character values. + See http://rfc.net/rfc2045.html for the base64 format. + */ --> + <xs:simpleType name="Base64"> + <xs:restriction base="xs:string"> + <xs:pattern value="[A-Za-z0-9+/ ]+"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** BaseBitmap + A reference to an image like a JPEG, GIF, or PNG; or a reference to a bitmap element + that has been drawn into with a drawTo element. + */ --> + <xs:simpleType name="BaseBitmap"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** BitmapEncoding + Used to specify the compression format for writing an image file with the snapshot element. + */ --> + <xs:simpleType name="BitmapEncoding"> + <xs:restriction base="xs:string"> + <!-- @pattern jpeg See http://www.jpeg.org/jpeg/ for more information about JPEG. --> + <xs:pattern value="jpeg"/> + <!-- @pattern png See http://www.libpng.org/pub/png/ for more information about PNG. --> + <xs:pattern value="png"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** BitmapFormat + Determines the number of bits per pixel in a bitmap. + */ --> + <xs:simpleType name="BitmapFormat"> + <xs:restriction base="xs:string"> + <xs:pattern value="none"/> + <!-- @pattern A1 1-bit per pixel, (0 is transparent, 1 is opaque). --> + <xs:pattern value="A1"/> + <!-- @pattern A8 8-bits per pixel, with only alpha specified (0 is transparent, 0xFF is opaque). --> + <xs:pattern value="A8"/> + <!-- @pattern Index8 8-bits per pixel, using a ColorTable element to specify the colors. --> + <xs:pattern value="Index8"/> + <!-- @pattern RGB16 16-bits per pixel, compile-time configured to be either 555 or 565. --> + <xs:pattern value="RGB16"/> + <!-- @pattern RGB32 32-bits per pixel, plus alpha. --> + <xs:pattern value="RGB32"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Boolean + Either "true" (non-zero) or "false" (zero). + */ --> + <xs:simpleType name="Boolean"> + <xs:restriction base="xs:string"> + <xs:pattern value="false"/> + <xs:pattern value="true"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Cap + The values for the strokeCap attribute. + */ --> + <xs:simpleType name="Cap"> + <xs:restriction base="xs:string"> + <!-- @pattern butt begin and end a contour with no extension --> + <xs:pattern value="butt"/> + <!-- @pattern round begin and end a contour with a semi-circle extension --> + <xs:pattern value="round"/> + <!-- @pattern square begin and end a contour with a half square extension --> + <xs:pattern value="square"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Color + A reference to a color element. + */ --> + <xs:simpleType name="Color"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** Displayable + A reference to any element: @list(Displayable) + */ --> + <xs:simpleType name="Displayable"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** DisplayableArray + An array of one or more element IDs. + */ --> + <xs:simpleType name="DisplayableArray"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** Drawable + A reference to an element that can be drawn: @list(Drawable) + */ --> + <xs:simpleType name="Drawable"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** DynamicString + Dynamic strings contain scripts that are re-evaluated each time the script is enabled. + */ --> + <xs:simpleType name="DynamicString"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** EventCode + Key codes that can trigger events, usually corresponding to physical buttons on the device. + */ --> + <xs:simpleType name="EventCode"> + <xs:restriction base="xs:string"> + <xs:pattern value="none"/> + <!-- @pattern up The up arrow. --> + <xs:pattern value="up"/> + <!-- @pattern down The down arrow. --> + <xs:pattern value="down"/> + <!-- @pattern left The left arrow. --> + <xs:pattern value="left"/> + <!-- @pattern right The right arrow. --> + <xs:pattern value="right"/> + <!-- @pattern back The back button (may not be present; the Backspace key on a PC). --> + <xs:pattern value="back"/> + <!-- @pattern end The end button (may not be present; the Esc key on a PC). --> + <xs:pattern value="end"/> + <!-- @pattern OK The OK button (the Enter key on a PC). --> + <xs:pattern value="OK"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** EventKind + Specifies how an event is triggered; by a key, when an animation ends, when the + document is loaded, or when this event is triggered by the user's C++ or XML. + */ --> + <xs:simpleType name="EventKind"> + <xs:restriction base="xs:string"> + <xs:pattern value="none"/> + <!-- @pattern keyChar A key corresponding to a Unichar value. --> + <xs:pattern value="keyChar"/> + <!-- @pattern keyPress A key with a particular function, such as an arrow key or the OK button. --> + <xs:pattern value="keyPress"/> + <!-- @pattern mouseDown Triggered when the primary mouse button is pressed. --> + <xs:pattern value="mouseDown"/> + <!-- @pattern mouseDrag Triggered when the primary mouse is moved while the button is pressed. --> + <xs:pattern value="mouseDrag"/> + <!-- @pattern mouseMove Triggered when the primary mouse is moved. --> + <xs:pattern value="mouseMove"/> + <!-- @pattern mouseUp Triggered when the primary mouse button is released. --> + <xs:pattern value="mouseUp"/> + <!-- @pattern onEnd Triggered when an event ends. --> + <xs:pattern value="onEnd"/> + <!-- @pattern onLoad Triggered when the document loads. --> + <xs:pattern value="onLoad"/> + <!-- @pattern user Triggered when a post element or C++ event is activated. --> + <xs:pattern value="user"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** EventMode + Specifies whether the event is delivered immediately to matching event element or deferred to + the application-wide event handler. + */ --> + <xs:simpleType name="EventMode"> + <xs:restriction base="xs:string"> + <!-- @pattern deferred Process the event using the host's event queue. --> + <xs:pattern value="deferred"/> + <!-- @pattern immediate Activate the event element immediately. --> + <xs:pattern value="immediate"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** FillType + Filled paths that self-intersect use the winding or evenOdd rule to determine whether the + overlaps are filled or are holes. + */ --> + <xs:simpleType name="FillType"> + <xs:restriction base="xs:string"> + <!-- @pattern winding Fill if the sum of edge directions is non-zero. --> + <xs:pattern value="winding"/> + <!-- @pattern evenOdd Fill if the sum of edges is an odd number. --> + <xs:pattern value="evenOdd"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** FilterType + Scaled bitmaps without a filter type set point-sample the source bitmap to determine the + destination pixels' colors. Bilinear and bicubic compute the values of intermediate pixels + by sampling the pixels around them. + */ --> + <xs:simpleType name="FilterType"> + <xs:restriction base="xs:string"> + <xs:pattern value="none"/> + <!-- @pattern bilinear Compute the pixel value as the linear interpolation of adjacent pixels. --> + <xs:pattern value="bilinear"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Float + A signed fractional value. + */ --> + <xs:simpleType name="Float"> + <xs:restriction base="xs:float"> + <xs:pattern value="[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** FloatArray + An array of one or more signed fractional values. + */ --> + <xs:simpleType name="FloatArray"> + <xs:restriction base="xs:float"> + <xs:pattern value="\[[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)( *, *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?))*\]"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** FromPathMode + A matrix computed from an offset along a path may include the point's position, the angle + tangent, or both. + . + */ --> + <xs:simpleType name="FromPathMode"> + <xs:restriction base="xs:string"> + <!-- @pattern normal Compute the matrix using the path's angle and position. --> + <xs:pattern value="normal"/> + <!-- @pattern angle Compute the matrix using only the path's angle. --> + <xs:pattern value="angle"/> + <!-- @pattern position Compute the matrix using only the path's position. --> + <xs:pattern value="position"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Int + A signed integer. + */ --> + <xs:simpleType name="Int"> + <xs:restriction base="xs:integer"/> + </xs:simpleType> + + <!-- /** IntArray + An array of one or more signed integer values. + */ --> + <xs:simpleType name="IntArray"> + <xs:restriction base="xs:integer"> + <xs:pattern value="\[[+-]?[0-9]+( *, *[+-]?[0-9]+)*\]"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Join + The edges of thick lines in a path are joined by extending the outer edges to form a miter, + or by adding a round circle at the intersection point, or by connecting the outer edges with a line + to form a blunt joint. + */ --> + <xs:simpleType name="Join"> + <xs:restriction base="xs:string"> + <!-- @pattern miter Extend the outer edges to form a miter. --> + <xs:pattern value="miter"/> + <!-- @pattern round Join the outer edges with a circular arc. --> + <xs:pattern value="round"/> + <!-- @pattern blunt Connect the outer edges with a line. --> + <xs:pattern value="blunt"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** MaskFilterBlurStyle + A blur can affect the inside or outside part of the shape, or it can affect both. The shape + itself can be drawn solid, or can be invisible. + */ --> + <xs:simpleType name="MaskFilterBlurStyle"> + <xs:restriction base="xs:string"> + <!-- @pattern normal Blur inside and outside. --> + <xs:pattern value="normal"/> + <!-- @pattern solid Solid inside, blur outside. --> + <xs:pattern value="solid"/> + <!-- @pattern outer Invisible inside, blur outside. --> + <xs:pattern value="outer"/> + <!-- @pattern inner Blur inside only.. --> + <xs:pattern value="inner"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** MaskFilter + The ID of a blur or emboss element. + */ --> + <xs:simpleType name="MaskFilter"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** Matrix + The ID of a matrix element. + */ --> + <xs:simpleType name="Matrix"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** MSec + A fractional second with millisecond resolution. + */ --> + <xs:simpleType name="MSec"> + <xs:restriction base="xs:float"/> + </xs:simpleType> + + <!-- /** Paint + The ID of a paint element. + */ --> + <xs:simpleType name="Paint"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** Path + The ID of a path element. + */ --> + <xs:simpleType name="Path"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** PathDirection + PathDirection determines if the path is traveled clockwise or counterclockwise. + */ --> + <xs:simpleType name="PathDirection"> + <xs:restriction base="xs:string"> + <!-- @pattern cw The path is traveled clockwise. --> + <xs:pattern value="cw"/> + <!-- @pattern ccw The path is traveled counterclockwise. --> + <xs:pattern value="ccw"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** PathEffect + The ID of a dash or discrete element. + */ --> + <xs:simpleType name="PathEffect"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** Point + A pair of signed values representing the x and y coordinates of a point. + */ --> + <xs:simpleType name="Point"> + <xs:restriction base="xs:string"> + <xs:pattern value="\[ *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?) *[ ,] *[+-]?([0-9]*\.[0-9]+|[0-9]+\.?)\]"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Rect + The ID of a rectangle element. + */ --> + <xs:simpleType name="Rect"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** Shader + The ID of a linear or radial gradient. + */ --> + <xs:simpleType name="Shader"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** String + A sequence of characters. + */ --> + <xs:simpleType name="String"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** Style + Geometry can be filled, stroked or both. + */ --> + <xs:simpleType name="Style"> + <xs:restriction base="xs:string"> + <!-- @pattern fill The interior of the geometry is filled with the paint's color. --> + <xs:pattern value="fill"/> + <!-- @pattern stroke The outline of the geometry is stroked with the paint's color. --> + <xs:pattern value="stroke"/> + <!-- @pattern strokeAndFill The interior is filled and outline is stroked with the paint's color. --> + <xs:pattern value="strokeAndFill"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Text + The ID of a text element. + */ --> + <xs:simpleType name="Text"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** TextBoxAlign + Multiple lines of text may be aligned to the start of the box, the center, or the end. + */ --> + <xs:simpleType name="TextBoxAlign"> + <xs:restriction base="xs:string"> + <!-- @pattern start The text begins within the upper left of the box. --> + <xs:pattern value="start"/> + <!-- @pattern center The text is positioned in the center of the box. --> + <xs:pattern value="center"/> + <!-- @pattern end The text ends within the lower right of the box. --> + <xs:pattern value="end"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** TextBoxMode + Fitting the text may optionally introduce line breaks. + */ --> + <xs:simpleType name="TextBoxMode"> + <xs:restriction base="xs:string"> + <!-- @pattern oneLine No additional linebreaks are added. --> + <xs:pattern value="oneLine"/> + <!-- @pattern lineBreak Line breaks may be added to fit the text to the box. --> + <xs:pattern value="lineBreak"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** TileMode + A shader describes how to draw within a rectangle. + Outside of the rectangle, the shader may be ignored, clamped on the edges, or repeated. + The repetitions may be mirrored from the original shader. + */ --> + <xs:simpleType name="TileMode"> + <xs:restriction base="xs:string"> + <!-- @pattern clamp The edge shader color is extended. --> + <xs:pattern value="clamp"/> + <!-- @pattern repeat The shader is repeated horizontally and vertically. --> + <xs:pattern value="repeat"/> + <!-- @pattern mirror The shader is mirrored horizontally and vertically. --> + <xs:pattern value="mirror"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Typeface + The ID of a typeface element. + */ --> + <xs:simpleType name="Typeface"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** UnknownArray + An array of values of any type. + */ --> + <xs:simpleType name="UnknownArray"> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <!-- /** Xfermode + The operation applied when drawing a color to the destination background. + */ --> + <xs:simpleType name="Xfermode"> + <xs:restriction base="xs:string"> + <!-- @pattern clear Set the destination alpha to zero and the destination color to black. --> + <xs:pattern value="clear"/> + <!-- @pattern src Set the destination to the source alpha and color. --> + <xs:pattern value="src"/> + <!-- @pattern dst Set the destination to the destination alpha and color. --> + <xs:pattern value="dst"/> + <!-- @pattern srcOver The default. Set the destination to the source color blended + with the destination by the source alpha. --> + <xs:pattern value="srcOver"/> + <!-- @pattern dstOver Set the destination to the destination color blended + with the source by the destination alpha. --> + <xs:pattern value="dstOver"/> + <!-- @pattern srcIn Set the destination to the source color scaled by the destination + alpha. --> + <xs:pattern value="srcIn"/> + <!-- @pattern dstIn Set the destination to the destination color scaled by the source + alpha. --> + <xs:pattern value="dstIn"/> + <!-- @pattern srcOut Set the destination to the source color scaled by the + inverse of the destination alpha. --> + <xs:pattern value="srcOut"/> + <!-- @pattern dstOut Set the destination to the destination color scaled by the + inverse of the source alpha. --> + <xs:pattern value="dstOut"/> + <!-- @pattern srcATop Set the destination to the source color times the destination alpha, + blended with the destination times the inverse of the source alpha. --> + <xs:pattern value="srcATop"/> + <!-- @pattern dstATop Set the destination to the destination color times the source alpha, + blended with the source times the inverse of the destination alpha. --> + <xs:pattern value="dstATop"/> + <!-- @pattern xor Set the destination to the destination color times the + inverse of the source alpha, + blended with the source times the inverse of the destination alpha. --> + <xs:pattern value="xor"/> + </xs:restriction> + </xs:simpleType> + + <!-- /** Math + Math provides functions and properties in the ECMAScript library to screenplay script expressions. + The Math element is always implicitly added at the top of every screenplay description, so + its functions and properties are always available. + */ --> + <xs:element name="Math"> + <xs:complexType> + <!-- @attribute E The value 2.718281828. --> + <xs:attribute name="E" type="Sk:Float"/> + <!-- @attribute LN10 The value 2.302585093. --> + <xs:attribute name="LN10" type="Sk:Float"/> + <!-- @attribute LN2 The value 0.693147181. --> + <xs:attribute name="LN2" type="Sk:Float"/> + <!-- @attribute LOG10E The value 0.434294482. --> + <xs:attribute name="LOG10E" type="Sk:Float"/> + <!-- @attribute LOG2E The value 1.442695041. --> + <xs:attribute name="LOG2E" type="Sk:Float"/> + <!-- @attribute PI The value 3.141592654. --> + <xs:attribute name="PI" type="Sk:Float"/> + <!-- @attribute SQRT1_2 The value 0.707106781. --> + <xs:attribute name="SQRT1_2" type="Sk:Float"/> + <!-- @attribute SQRT2 The value 1.414213562. --> + <xs:attribute name="SQRT2" type="Sk:Float"/> + <!-- @attribute abs A function that returns the absolute value of its argument. --> + <xs:attribute name="abs" type="Sk:Float"/> + <!-- @attribute acos A function that returns the arc cosine of its argument. --> + <xs:attribute name="acos" type="Sk:Float"/> + <!-- @attribute asin A function that returns the arc sine of its argument. --> + <xs:attribute name="asin" type="Sk:Float"/> + <!-- @attribute atan A function that returns the arc tan of its argument. --> + <xs:attribute name="atan" type="Sk:Float"/> + <!-- @attribute atan2 A function that returns the arc tan of the ratio of its two arguments. --> + <xs:attribute name="atan2" type="Sk:Float"/> + <!-- @attribute ceil A function that returns the rounded up value of its argument. --> + <xs:attribute name="ceil" type="Sk:Float"/> + <!-- @attribute cos A function that returns the cosine of its argument. --> + <xs:attribute name="cos" type="Sk:Float"/> + <!-- @attribute exp A function that returns E raised to a power (the argument). --> + <xs:attribute name="exp" type="Sk:Float"/> + <!-- @attribute floor A function that returns the rounded down value of its argument. --> + <xs:attribute name="floor" type="Sk:Float"/> + <!-- @attribute log A function that returns the natural logarithm its argument. --> + <xs:attribute name="log" type="Sk:Float"/> + <!-- @attribute max A function that returns the largest of any number of arguments. --> + <xs:attribute name="max" type="Sk:Float"/> + <!-- @attribute min A function that returns the smallest of any number of arguments. --> + <xs:attribute name="min" type="Sk:Float"/> + <!-- @attribute pow A function that returns the first argument raised to the power of the second argument. --> + <xs:attribute name="pow" type="Sk:Float"/> + <!-- @attribute random A function that returns a random value from zero to one. + (See also the <random> element.) --> + <xs:attribute name="random" type="Sk:Float"/> + <!-- @attribute round A function that returns the rounded value of its argument. --> + <xs:attribute name="round" type="Sk:Float"/> + <!-- @attribute sin A function that returns the sine of its argument. --> + <xs:attribute name="sin" type="Sk:Float"/> + <!-- @attribute sqrt A function that returns the square root of its argument. --> + <xs:attribute name="sqrt" type="Sk:Float"/> + <!-- @attribute tan A function that returns the tangent of its argument. --> + <xs:attribute name="tan" type="Sk:Float"/> + </xs:complexType> + </xs:element> + + <!-- /** Number + Number provides properties in the ECMAScript library to screenplay script expressions. + The Number element is always implicitly added at the top of every screenplay description, so + its properties are always available. + */ --> + <xs:element name="Number"> + <xs:complexType> + <!-- @attribute MAX_VALUE The maximum number value; approximately 32767.999985 fixed point, + 3.4028235e+38 floating point. --> + <xs:attribute name="MAX_VALUE" type="Sk:Float"/> + <!-- @attribute MIN_VALUE The minimum number value; approximately 0.000015 fixed point, + 1.1754944e-38 floating point. --> + <xs:attribute name="MIN_VALUE" type="Sk:Float"/> + <!-- @attribute NEGATIVE_INFINITY The most negative number value. Fixed point does not + have a value for negative infinity, and approximates it with -32767.999985. --> + <xs:attribute name="NEGATIVE_INFINITY" type="Sk:Float"/> + <!-- @attribute NaN A bit pattern representing "Not a Number". Fixed point does not + have a value for NaN, and approximates it with -32768. --> + <xs:attribute name="NaN" type="Sk:Float"/> + <!-- @attribute POSITIVE_INFINITY The greatest positive number value. Fixed point does not + have a value for positive infinity, and approximates it with 32767.999985. --> + <xs:attribute name="POSITIVE_INFINITY" type="Sk:Float"/> + </xs:complexType> + </xs:element> + + <!-- /** add + Add references a drawable element, and adds it to the display list. + If where and offset are omitted, the element is appended to the end of the display list. + If where is specified, the element is inserted at the first occurance of where in the display list. + If offset and where are specified, the element is inserted at where plus offset. + A positive offset without where inserts the element at the start of the list plus offset. + A negative offset without where inserts the element at the end of the list minus offset. + */ --> + <xs:element name="add"> + <xs:complexType> + <!-- @attribute mode If indirect (the default), keep the add element in the display list, + and draw the add's use element. If immediate, put the add's use element in the display list. --> + <xs:attribute name="mode" type="Sk:AddMode"/> + <!-- @attribute offset The offset added to the insert index. --> + <xs:attribute name="offset" type="Sk:Int"/> + <!-- @attribute use The drawable element to add to the display list. --> + <xs:attribute name="use" type="Sk:Drawable"/> + <!-- @attribute where The drawable element marking where to insert. --> + <xs:attribute name="where" type="Sk:Drawable"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** addCircle + AddCircle adds a closed circle to the parent path element. + */ --> + <xs:element name="addCircle"> + <xs:complexType> + <!-- @attribute direction One of @pattern. @patternDescription --> + <xs:attribute name="direction" type="Sk:PathDirection"/> + <!-- @attribute radius The distance from the center to the edge of the circle. --> + <xs:attribute name="radius" type="Sk:Float"/> + <!-- @attribute x The x coordinate of the circle's center. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The y coordinate of the circle's center.--> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** addOval + AddOval adds a closed oval described by its bounding box to the parent path element. + */ --> + <xs:element name="addOval"> + <xs:complexType> + <!-- @attribute direction One of @pattern. @patternDescription --> + <xs:attribute name="direction" type="Sk:PathDirection"/> + <!-- @attribute bottom The bottom edge of the oval's bounding box. --> + <xs:attribute name="bottom" type="Sk:Float"/> + <!-- @attribute left The left edge of the oval's bounding box. --> + <xs:attribute name="left" type="Sk:Float"/> + <!-- @attribute right The right edge of the oval's bounding box. --> + <xs:attribute name="right" type="Sk:Float"/> + <!-- @attribute top The top edge of the oval's bounding box. --> + <xs:attribute name="top" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** addPath + AddPath adds a path to the parent path element. + An optional matrix may transform the path as it is added. + */ --> + <xs:element name="addPath"> + <xs:complexType> + <!-- @attribute matrix The matrix applied to the path as it is added. --> + <xs:attribute name="matrix" type="Sk:Matrix"/> + <!-- @attribute path The path to add. --> + <xs:attribute name="path" type="Sk:Path"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** addRect + AddRect adds a closed rectangle to the parent path element. + */ --> + <xs:element name="addRect"> + <xs:complexType> + <!-- @attribute direction One of @pattern. @patternDescription --> + <xs:attribute name="direction" type="Sk:PathDirection"/> + <!-- @attribute bottom The bottom edge of the rectangle. --> + <xs:attribute name="bottom" type="Sk:Float"/> + <!-- @attribute left The left edge of the rectangle. --> + <xs:attribute name="left" type="Sk:Float"/> + <!-- @attribute right The right edge of the rectangle. --> + <xs:attribute name="right" type="Sk:Float"/> + <!-- @attribute top" The top" edge of the rectangle. --> + <xs:attribute name="top" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** addRoundRect + AddRoundRect adds a closed rounded rectangle to the parent path element. + */ --> + <xs:element name="addRoundRect"> + <xs:complexType> + <!-- @attribute direction One of @pattern. @patternDescription --> + <xs:attribute name="direction" type="Sk:PathDirection"/> + <!-- @attribute bottom The bottom edge of the rounded rectangle's bounding box. --> + <xs:attribute name="bottom" type="Sk:Float"/> + <!-- @attribute left The left edge of the rounded rectangle's bounding box. --> + <xs:attribute name="left" type="Sk:Float"/> + <!-- @attribute right The right edge of the rounded rectangle's bounding box. --> + <xs:attribute name="right" type="Sk:Float"/> + <!-- @attribute top The top edge of the rounded rectangle's bounding box. --> + <xs:attribute name="top" type="Sk:Float"/> + <!-- @attribute rx The X-radius of the oval used to round the corners. --> + <xs:attribute name="rx" type="Sk:Float"/> + <!-- @attribute ry The Y-radius of the oval used to round the corners. --> + <xs:attribute name="ry" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** animate + Animate varies the value of an element's attribute over time. + The animation may vary starting at the 'from' attribute, and ending at the 'to' attribute, + or may compute the value using the 'formula' attribute. + */ --> + <xs:element name="animate"> + <xs:complexType> + <!-- @attribute begin An optional offset that must elapse before the animation begins. The apply + begin attribute is added to any animator's begin attribute. --> + <xs:attribute name="begin" type="Sk:MSec"/> + <!-- @attribute blend Specifies how the from and to values are blended. A value from 0.0 to + 1.0 specifies a cubic lag/log/lag blend (slow to change at the beginning and end); the closer + blend is to 1.0, the more linear the blend. If omitted, the blend is linear. --> + <xs:attribute name="blend" type="Sk:FloatArray"/> + <!-- @attribute dur The duration of the animation in milliseconds. --> + <xs:attribute name="dur" type="Sk:MSec"/> + <!-- @attribute dynamic If true, restart the animation if any of the simple values the 'from', 'formula', + 'lval', or 'to' attributes reference are changed. Simple values are contained by the array, boolean, float, int, + and string elements. --> + <xs:attribute name="dynamic" type="Sk:Boolean" /> + <!-- @attribute field The attribute to animate. --> + <xs:attribute name="field" type="Sk:String"/> + <!-- @attribute formula A script to execute over time to compute the field's value. Typically, + the fomula is a script expression which includes a reference to the time attribute of the + containing apply element. Requires a dur. For animations that do not stop, set dur="Number.POSITIVE_INFINITY" --> + <xs:attribute name="formula" type="Sk:DynamicString"/> + <!-- @attribute from The starting value (requires a 'to' attribute) --> + <xs:attribute name="from" type="Sk:DynamicString"/> + <!-- @attribute lval An expression evaluating to the attribute to animate. + If present, lval overrides 'field'. The expression is typically an array element, + e.g. lval="x[y]" . --> + <xs:attribute name="lval" type="Sk:DynamicString"/> + <!-- @attribute mirror If true, reverses the interpolated value during even repeat cycles. --> + <xs:attribute name="mirror" type="Sk:Boolean"/> + <!-- @attribute repeat Specifies the number of times to repeat the animation. + (May be fractional.) --> + <xs:attribute name="repeat" type="Sk:Float"/> + <!-- @attribute reset If true, the computed value is the initial value after the + animation is complete. If false, or by default, the computed value is the final value + after the animation is complete. --> + <xs:attribute name="reset" type="Sk:Boolean"/> + <!-- @attribute step When the apply's attribute mode="immediate" or "create", the step attribute can be read by + script to determine the current animation iteration. --> + <xs:attribute name="step" type="Sk:Int" /> + <!-- @attribute target The element to animate. By default, the element contained by the apply + or referenced by the apply's scope attribute is the animate target. --> + <xs:attribute name="target" type="Sk:DynamicString"/> + <!-- @attribute to The ending value (requires a 'from' attribute) --> + <xs:attribute name="to" type="Sk:DynamicString"/> + <!-- @attribute values [Depreciated] --> + <xs:attribute name="values" type="Sk:DynamicString"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** apply + Apply changes one or more attributes of an element. + Apply either contains one displayable element or references the element scoping the change + with the 'scope' attribute. Apply either contains one animator element or references it with + the 'animator' attribute. + In the display list, apply draws the element it scopes after evaluating the animation. + */ --> + <xs:element name="apply"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="1"> + <xs:element ref="Sk:animate"/> + <xs:element ref="Sk:set" /> + <!-- not quite right; want to say 'one of the above, one of the below' + </xs:choice> + <xs:choice minOccurs="0" maxOccurs="1"> + --> + <xs:element ref="Sk:add"/> + <xs:element ref="Sk:array"/> + <xs:element ref="Sk:apply"/> + <xs:element ref="Sk:bitmap"/> + <xs:element ref="Sk:boolean"/> + <xs:element ref="Sk:bounds"/> + <!-- <xs:element ref="Sk3D:camera"/> --> + <xs:element ref="Sk:clear"/> + <xs:element ref="Sk:clip"/> + <xs:element ref="Sk:color"/> + <xs:element ref="Sk:drawTo"/> + <xs:element ref="Sk:float"/> + <xs:element ref="Sk:full"/> + <xs:element ref="Sk:group"/> + <xs:element ref="Sk:image"/> + <xs:element ref="Sk:int"/> + <xs:element ref="Sk:line"/> + <xs:element ref="Sk:matrix"/> + <xs:element ref="Sk:move"/> + <xs:element ref="Sk:oval"/> + <xs:element ref="Sk:paint"/> + <!-- <xs:element ref="Sk:patch"/> --> + <xs:element ref="Sk:path"/> + <xs:element ref="Sk:point"/> + <xs:element ref="Sk:polygon"/> + <xs:element ref="Sk:polyline"/> + <xs:element ref="Sk:post"/> + <xs:element ref="Sk:random"/> + <xs:element ref="Sk:rect"/> + <xs:element ref="Sk:remove"/> + <xs:element ref="Sk:replace"/> + <xs:element ref="Sk:roundRect"/> + <xs:element ref="Sk:save"/> + <xs:element ref="Sk:snapshot"/> + <xs:element ref="Sk:string"/> + <xs:element ref="Sk:text"/> + <xs:element ref="Sk:textBox"/> + <xs:element ref="Sk:textOnPath"/> + <xs:element ref="Sk:textToPath"/> + </xs:choice> + <!-- @attribute animator The description of how the element is changed over time. --> + <xs:attribute name="animator" type="Sk:Animate"/> + <!-- @attribute begin An optional offset that must elapse before the animation begins. The apply + begin attribute is added to any animator's begin attribute. --> + <xs:attribute name="begin" type="Sk:MSec" /> + <!-- @attribute dontDraw Edits an element's attribute without drawing it; for instance, + to edit a clip's rectangle without drawing the rectangle, set dontDraw="true". --> + <xs:attribute name="dontDraw" type="Sk:Boolean"/> + <!-- @attribute dynamicScope The location in the display list where animations are stored. Use + dynamicScope instead of scope if a script expression with potentially different values is desired to + describe the scope. --> + <xs:attribute name="dynamicScope" type="Sk:String"/> + <!-- @attribute interval The optional time interval from one animation frame to the next. --> + <xs:attribute name="interval" type="Sk:MSec" /> + <!-- @attribute mode One of @pattern. @patternDescription --> + <xs:attribute name="mode" type="Sk:ApplyMode"/> + <!-- @attribute pickup Starts the animation at the current target's attribute value. Enabling + 'pickup' permits omitting the 'from' attribute of the animator. --> + <xs:attribute name="pickup" type="Sk:Boolean"/> + <!-- @attribute restore If true, multiple references to the same apply statement save and + restore the interpolated target values. --> + <xs:attribute name="restore" type="Sk:Boolean"/> + <!-- @attribute scope The location in the display list where animations are stored. --> + <xs:attribute name="scope" type="Sk:Drawable"/> + <!-- @attribute step When mode="immediate" or "create", the step attribute can be read by + script to determine the current animation iteration. --> + <xs:attribute name="step" type="Sk:Int" /> + <!-- @attribute steps When mode="immediate", the number of times the animation + is stepped. The animation iterates 'steps' times plus one. --> + <xs:attribute name="steps" type="Sk:Int" /> + <!-- @attribute time When read from script, returns the animation time. Typically used by + an animate element's formula attribute. --> + <xs:attribute name="time" type="Sk:MSec" /> + <!-- @attribute transition One of @pattern. @patternDescription --> + <xs:attribute name="transition" type="Sk:ApplyTransition"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** array + Array contains an array of values of the same type. The values may be + numbers or strings. + */ --> + <xs:element name="array"> + <xs:complexType> + <!-- @attribute length The number of elements in the array (read only). --> + <xs:attribute name="length" type="Sk:Int"/> + <!-- @attribute values The elements in the array. --> + <xs:attribute name="values" type="Sk:UnknownArray"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** bitmap + Bitmap describes a rectangle of pixels. + Use the <drawTo> element to draw to a bitmap. + Add the bitmap to the display list to draw from a bitmap. + */ --> + <xs:element name="bitmap"> + <xs:complexType> + <!-- @attribute erase The color, including the alpha, the bitmap is intially set to. --> + <xs:attribute name="erase" type="Sk:ARGB"/> + <!-- @attribute format One of @pattern. @patternDescription --> + <xs:attribute name="format" type="Sk:BitmapFormat"/> + <!-- @attribute height The height of the bitmap in pixels. --> + <xs:attribute name="height" type="Sk:Int"/> + <!-- @attribute rowBytes The number of byte describing each row of pixels (optional). --> + <xs:attribute name="rowBytes" type="Sk:Int"/> + <!-- @attribute width The height of the width in pixels. --> + <xs:attribute name="width" type="Sk:Int"/> + <!-- @attribute x The left edge of the bitmap in unit space. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The top edge of teh bitmap in unit space. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** bitmapShader + BitmapShader sets the paint shader to draw the bitmap as a texture. + */ --> + <xs:element name="bitmapShader"> + <xs:complexType> + <xs:choice > + <xs:element ref="Sk:image" minOccurs="0" /> + <xs:element ref="Sk:matrix" minOccurs="0" /> + </xs:choice> + <!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. --> + <xs:attribute name="matrix" type="Sk:Matrix"/> + <!-- @attribute tileMode One of @pattern. @patternDescription --> + <xs:attribute name="tileMode" type="Sk:TileMode"/> + <!-- @attribute filterType The bitmap filter to employ, one of @pattern. --> + <xs:attribute name="filterType" type="Sk:FilterType"/> + <!-- @attribute image The bitmap to draw. --> + <xs:attribute name="image" type="Sk:BaseBitmap"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + + <!-- /** blur + Blur describes an image filter in the paint that blurs the drawn geometry. + */ --> + <xs:element name="blur"> + <xs:complexType> + <!-- @attribute blurStyle One of @pattern. @patternDescription --> + <xs:attribute name="blurStyle" type="Sk:MaskFilterBlurStyle"/> + <!-- @attribute radius The extent of the filter effect in unit space. If the radius is less + than zero, the blur has no effect. --> + <xs:attribute name="radius" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** boolean + Boolean contains an boolean. The boolean element cannot be added to a display list, but can + by set by animations and read by any attribute definition. An boolean element may be referenced, + for instance, by a group's condition attribute to make an animation conditionally execute. + */ --> + <xs:element name="boolean"> + <xs:complexType> + <!-- @attribute value The contained boolean. --> + <xs:attribute name="value" type="Sk:Boolean"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** bounds + Bounds describes a bounding box that is not drawn. Bounds is used to specify a rectangle to + invalidate or record whether the specified area was drawn. + The width and height attribute compute the rectangle's right and bottom edges when the rectangle + description is first seen. Animating the rectangle's left or top will not recompute the right or bottom + if the width or height have been specified. + */ --> + <xs:element name="bounds"> + <xs:complexType> + <!-- @attribute bottom The bottom edge of the rectangle. --> + <xs:attribute name="bottom" type="Sk:Float"/> + <!-- @attribute height The height of the rectangle. Setting height computes the + bottom attribute from the top attribute. --> + <xs:attribute name="height" type="Sk:Float"/> + <!-- @attribute inval If set to true, union the drawn bounds to compute an inval area. --> + <xs:attribute name="inval" type="Sk:Boolean"/> + <!-- @attribute left The left edge of the rectangle. --> + <xs:attribute name="left" type="Sk:Float"/> + <!-- @attribute needsRedraw Set to true if last draw was visible. --> + <xs:attribute name="needsRedraw" type="Sk:Boolean"/> + <!-- @attribute right The right edge of the rectangle. --> + <xs:attribute name="right" type="Sk:Float"/> + <!-- @attribute top The top edge of the rectangle. --> + <xs:attribute name="top" type="Sk:Float"/> + <!-- @attribute width The width of the rectangle. --> + <xs:attribute name="width" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** clear + Clear removes all entries in the display list. + */ --> + <xs:element name="clear"> + <xs:complexType> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** clip + Clip sets the canvas to clip drawing to an element's geometry. + A clip element may contain an element or reference an element with the path or + rectangle attributes. To make the clip unrestricted, enclose a 'full' element. + */ --> + <xs:element name="clip"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="1"> + <xs:element ref="Sk:full"/> + <xs:element ref="Sk:rect"/> + <xs:element ref="Sk:path"/> + <xs:element ref="Sk:polygon"/> + <xs:element ref="Sk:polyline"/> + </xs:choice> + <!-- @attribute path A path-derived element to clip to: either an oval, + a path, a polygon, a polyline, or a roundRect. --> + <xs:attribute name="path" type="Sk:Path"/> + <!-- @attribute rect A rectangle element to clip to. --> + <xs:attribute name="rect" type="Sk:Rect"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** close + Close connects the last point in the path's contour to the first if the contour is not already closed. + */ --> + <xs:element name="close"> + <xs:complexType> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** color + Color describes a color in RGB space or HSV space, and its alpha (transparency). + */ --> + <xs:element name="color"> + <xs:complexType> + <!-- @attribute alpha The alpha component, which describes transparency. + Alpha ranges from 0.0 (transparent) to 1.0 (completely opaque). --> + <xs:attribute name="alpha" type="Sk:Float"/> + <!-- @attribute blue The blue component of an RGB color. Blue ranges from 0 to 255. --> + <xs:attribute name="blue" type="Sk:Float"/> + <!-- @attribute color The complete color. The color can be specified by name, + by hexadecimal value, or with the rgb function. --> + <xs:attribute name="color" type="Sk:ARGB"/> + <!-- @attribute green The green component of an RGB color. Green ranges from 0 to 255. --> + <xs:attribute name="green" type="Sk:Float"/> + <!-- @attribute hue The hue component of an HSV color. Hue ranges from 0 to 360. --> + <xs:attribute name="hue" type="Sk:Float"/> + <!-- @attribute red The red component of an RGB color. Red ranges from 0 to 255. --> + <xs:attribute name="red" type="Sk:Float"/> + <!-- @attribute saturation The saturation component of an HSV color. Saturation ranges from 0 to 1. --> + <xs:attribute name="saturation" type="Sk:Float"/> + <!-- @attribute value The value component of an HSV color. Value ranges from 0 to 1. --> + <xs:attribute name="value" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** cubicTo + CubicTo adds a cubic to the path, using the last point in the path as the first point of the cubic. + */ --> + <xs:element name="cubicTo"> + <xs:complexType> + <!-- @attribute x1 The x position of the first off-curve point. --> + <xs:attribute name="x1" type="Sk:Float"/> + <!-- @attribute x2 The x position of the second off-curve point. --> + <xs:attribute name="x2" type="Sk:Float"/> + <!-- @attribute x3 The x position of the final on-curve point. --> + <xs:attribute name="x3" type="Sk:Float"/> + <!-- @attribute y1 The y position of the first off-curve point. --> + <xs:attribute name="y1" type="Sk:Float"/> + <!-- @attribute y2 The y position of the second off-curve point. --> + <xs:attribute name="y2" type="Sk:Float"/> + <!-- @attribute y3 The y position of the final on-curve point. --> + <xs:attribute name="y3" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** dash + Dash describes an array of dashes and gaps that describe how the paint strokes lines, + rectangles, and paths. The intervals, phase, and dashed path are all measured in the same + unit space. The phase and distance between dashes is unaffected by the paint's stroke width. + */ --> + <xs:element name="dash"> + <xs:complexType> + <!-- @attribute intervals An array of floats that alternately describe the lengths of + dashes and gaps. Intervals must contain an even number of entries. --> + <xs:attribute name="intervals" type="Sk:FloatArray"/> + <!-- @attribute phase Phase advances the placement of the first dash. A positive phase + preceeds the first dash with a gap. A negative phase shortens the length of the first dash. --> + <xs:attribute name="phase" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** data + Data provides metadata to an event. The metadata may be an integer, a float, + or a string. + */ --> + <xs:element name="data"> + <xs:complexType> + <!-- @attribute float The float value associated with the metadata. --> + <xs:attribute name="float" type="Sk:Float"/> + <!-- @attribute initialized A read-only value set to false (unused by data). --> + <xs:attribute name="initialized" type="Sk:Boolean"/> + <!-- @attribute int The integer value associated with the metadata. --> + <xs:attribute name="int" type="Sk:Int"/> + <!-- @attribute name The name of the metadata. This is the name of the data. --> + <xs:attribute name="name" type="Sk:String"/> + <!-- @attribute string The string value associated with the metadata. --> + <xs:attribute name="string" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** discrete + Discrete alters the edge of the stroke randomly. Discrete is a path effect, and only has an + effect when referenced from a paint.. A <pathEffect/> + element with no attributes will dissable discrete. + */ --> + <xs:element name="discrete"> + <xs:complexType> + <!-- @attribute deviation The amount of wobble in the stroke. --> + <xs:attribute name="deviation" type="Sk:Float"/> + <!-- @attribute segLength The length of wobble in the stroke. --> + <xs:attribute name="segLength" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** drawTo + DrawTo images to a bitmap. The bitmap can be added to the display list + to draw the composite image. + DrawTo can be used as an offscreen to speed complicated animations, and + for bitmap effects such as pixelated zooming. + DrawTo can only reference a single drawable element. Use <add>, + <group>, or <save> to draw multiple elements with <drawTo>. + */ --> + <xs:element name="drawTo"> + <xs:complexType> + <xs:choice maxOccurs="unbounded" > + <xs:element ref="Sk:add"/> + <xs:element ref="Sk:apply"/> + <xs:element ref="Sk:bitmap"/> + <xs:element ref="Sk:bounds"/> + <!-- <xs:element ref="Sk3D:camera"/> --> + <xs:element ref="Sk:clear"/> + <xs:element ref="Sk:clip"/> + <xs:element ref="Sk:color"/> + <xs:element ref="Sk:full"/> + <xs:element ref="Sk:group"/> + <xs:element ref="Sk:image"/> + <xs:element ref="Sk:line"/> + <xs:element ref="Sk:matrix"/> + <xs:element ref="Sk:move"/> + <xs:element ref="Sk:oval"/> + <xs:element ref="Sk:paint"/> + <!-- <xs:element ref="Sk:patch"/> --> + <xs:element ref="Sk:path"/> + <xs:element ref="Sk:point"/> + <xs:element ref="Sk:polygon"/> + <xs:element ref="Sk:polyline"/> + <xs:element ref="Sk:rect"/> + <xs:element ref="Sk:remove"/> + <xs:element ref="Sk:replace"/> + <xs:element ref="Sk:roundRect"/> + <xs:element ref="Sk:save"/> + <xs:element ref="Sk:text"/> + <xs:element ref="Sk:textBox"/> + <xs:element ref="Sk:textOnPath"/> + <xs:element ref="Sk:textToPath"/> + </xs:choice> + <!-- @attribute drawOnce If set, the drawTo will only draw a single time. --> + <xs:attribute name="drawOnce" type="Sk:Boolean"/> + <!-- @attribute use The bitmap to draw into. --> + <xs:attribute name="use" type="Sk:bitmap"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** dump + Dump prints a list of the items in the display list and all items' + children to the debug console. Dump is only available in Debug + builds. */ --> + <xs:element name="dump"> + <xs:complexType> + <!-- @attribute displayList Dumps the current display list if true. The display list is also + dumped if dump has no attributes. --> + <xs:attribute name="displayList" type="Sk:Boolean"/> + <!-- @attribute eventList Dumps the list of events, both enabled and disabled. --> + <xs:attribute name="eventList" type="Sk:Boolean"/> + <!-- @attribute events Outputs each event element as it is enabled. --> + <xs:attribute name="events" type="Sk:Boolean"/> + <!-- @attribute groups Outputs each group element as its condition is evaluated. --> + <xs:attribute name="groups" type="Sk:Boolean"/> + <!-- @attribute name Outputs the values associated with a single named element. --> + <xs:attribute name="name" type="Sk:String"/> + <!-- @attribute posts Outputs each post element as it is enabled. --> + <xs:attribute name="posts" type="Sk:Boolean"/> + <!-- @attribute script Evaluates the provided script --> + <xs:attribute name="script" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** emboss + PRELIMINARY [to be replaced with SkEmbossMaskFilter.h doxyfomation + at some point] + Emboss applies a mask filter to the paint that makes bias the object's color + towards white or black depending on the normals of the path contour, giving + the shape a 3D raised or depressed effect. + Embossing is replaced by subsequent mask filter elements, or + disabled a negative radius, or by an empty <mask filter> element. + */ --> + <xs:element name="emboss"> + <xs:complexType> + <!-- @attribute ambient The amount of ambient light, from 0 to 1. --> + <xs:attribute name="ambient" type="Sk:Float"/> + <!-- @attribute direction The direction of the light source, as descibed by a 3D vector. + (The vector is normalized to a unit length of 1.0.) --> + <xs:attribute name="direction" type="Sk:FloatArray"/> + <!-- @attribute radius The extent of the filter effect in unit space. If the radius is less + than zero, the emboss has no effect. --> + <xs:attribute name="radius" type="Sk:Float"/> + <!-- @attribute specular The expotential intensity of the light, from 0 to 1. + Each increase of 0.0625 doubles the intensity. --> + <xs:attribute name="specular" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** event + Event contains a series of actions performed each time the event's criteria are satisfied. + These actions may modify the display list, may enable animations which in turn modify + elements' attributes, and may post other events. + */ --> + <xs:element name="event"> + <xs:complexType> + <xs:choice maxOccurs="unbounded" > + <xs:element ref="Sk:add"/> + <xs:element ref="Sk:apply"/> + <xs:element ref="Sk:array"/> + <xs:element ref="Sk:bitmap"/> + <xs:element ref="Sk:boolean"/> + <xs:element ref="Sk:bounds"/> + <!-- <xs:element ref="Sk3D:camera"/> --> + <xs:element ref="Sk:clear"/> + <xs:element ref="Sk:clip"/> + <xs:element ref="Sk:color"/> + <xs:element ref="Sk:drawTo"/> + <xs:element ref="Sk:dump"/> + <xs:element ref="Sk:float"/> + <xs:element ref="Sk:full"/> + <xs:element ref="Sk:group"/> + <xs:element ref="Sk:hitClear"/> + <xs:element ref="Sk:hitTest"/> + <xs:element ref="Sk:image"/> + <xs:element ref="Sk:input"/> + <xs:element ref="Sk:int"/> + <xs:element ref="Sk:line"/> + <xs:element ref="Sk:matrix"/> + <xs:element ref="Sk:move"/> + <xs:element ref="Sk:movie"/> + <xs:element ref="Sk:oval"/> + <xs:element ref="Sk:paint"/> + <!-- <xs:element ref="Sk:patch"/> --> + <xs:element ref="Sk:path"/> + <xs:element ref="Sk:point"/> + <xs:element ref="Sk:polygon"/> + <xs:element ref="Sk:polyline"/> + <xs:element ref="Sk:post"/> + <xs:element ref="Sk:random"/> + <xs:element ref="Sk:rect"/> + <xs:element ref="Sk:remove"/> + <xs:element ref="Sk:replace"/> + <xs:element ref="Sk:roundRect"/> + <xs:element ref="Sk:save"/> + <xs:element ref="Sk:snapshot"/> + <xs:element ref="Sk:string"/> + <xs:element ref="Sk:text"/> + <xs:element ref="Sk:textBox"/> + <xs:element ref="Sk:textOnPath"/> + <xs:element ref="Sk:textToPath"/> + </xs:choice> + <!-- @attribute code The key code to match to a key press event, one of @pattern. + If the code is set to @pattern[0], the event is never activated. --> + <xs:attribute name="code" type="Sk:EventCode"/> + <!-- @attribute disable If true, the event cannot be activated. By default false.. --> + <xs:attribute name="disable" type="Sk:Boolean"/> + <!-- @attribute key The character code to match to a key down event. + When read, the key that activated this event. --> + <xs:attribute name="key" type="Sk:String"/> + <!-- @attribute keys A dash-separated continuous range of character codes to match + to a key down event. Read the key attribute to determine the key that activated this event. --> + <xs:attribute name="keys" type="Sk:String"/> <!-- single or range of keys --> + <!-- @attribute kind The event kind that activates this event, one of @pattern. + If kind equals keyChar, either attribute key or keys is expected. + If kind equals keyPress, attribute code is expected. + If kind equals onEnd, attribute target is expected. + If kind equals onLoad, the event is activated when the document containing the event + is loaded. The onLoad attribute cannot be activated through a post event. + If kind equals user, the event is activated when the posted event targets this event's ID. --> + <xs:attribute name="kind" type="Sk:EventKind"/> + <!-- @attribute target The element to listen to which activates this event. --> + <xs:attribute name="target" type="Sk:String" /> + <!-- @attribute x For click events, the x-coordinate of the click. --> + <xs:attribute name="x" type="Sk:Float" /> + <!-- @attribute y For click events, the y-coordinate of the click. --> + <xs:attribute name="y" type="Sk:Float" /> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** float + Float contains a signed fractional value. The float element cannot be added to a display list, + but can be set by animations and read by any attribute definition. + */ --> + <xs:element name="float"> + <xs:complexType> + <!-- @attribute value The contained float. --> + <xs:attribute name="value" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** fromPath + FromPath concatenates the parent matrix with a new matrix + that maps a unit vector to a point on the given path. + A fromPath element may contain a path element, or may refer to a previously + defined path element with the path attribute. + */ --> + <xs:element name="fromPath"> + <xs:complexType> + <xs:choice > + <!-- @element path The path to evaluate. --> + <xs:element ref="Sk:path" minOccurs="0" /> + </xs:choice> + <!-- @attribute mode One of @pattern. + If mode is set to normal, the matrix maps the unit vector's angle and position. + If mode is set to angle, the matrix maps only the unit vector's angle. + If mode is set to position, the matrix maps only the unit vector's position. --> + <xs:attribute name="mode" type="Sk:FromPathMode"/> + <!-- @attribute offset The distance along the path to evaluate. --> + <xs:attribute name="offset" type="Sk:Float"/> + <!-- @attribute path The path to evaluate. --> + <xs:attribute name="path" type="Sk:Path"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** full + Full paints the entire canvas to the limit of the canvas' clip. + */ --> + <xs:element name="full"> + <xs:complexType> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** group + The group element collects a series of elements into a group. The group can be referenced + or defined within elements, like apply, which operate on any kind of element. Groups + may contain groups. An element in a group draws identically to an element outside a group. + */ --> + <xs:element name="group"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <xs:element ref="Sk:add"/> + <xs:element ref="Sk:apply"/> + <xs:element ref="Sk:array"/> + <xs:element ref="Sk:bitmap"/> + <xs:element ref="Sk:boolean"/> + <xs:element ref="Sk:bounds"/> + <!-- <xs:element ref="Sk3D:camera"/> --> + <xs:element ref="Sk:clear"/> + <xs:element ref="Sk:clip"/> + <xs:element ref="Sk:drawTo"/> + <xs:element ref="Sk:float"/> + <xs:element ref="Sk:full"/> + <xs:element ref="Sk:group"/> + <xs:element ref="Sk:hitClear"/> + <xs:element ref="Sk:hitTest"/> + <xs:element ref="Sk:image"/> + <xs:element ref="Sk:int"/> + <xs:element ref="Sk:line"/> + <xs:element ref="Sk:matrix"/> + <xs:element ref="Sk:move"/> + <xs:element ref="Sk:oval"/> + <xs:element ref="Sk:paint"/> + <!-- <xs:element ref="Sk:patch"/> --> + <xs:element ref="Sk:path"/> + <xs:element ref="Sk:point"/> + <xs:element ref="Sk:polygon"/> + <xs:element ref="Sk:polyline"/> + <xs:element ref="Sk:post"/> + <xs:element ref="Sk:random"/> + <xs:element ref="Sk:rect"/> + <xs:element ref="Sk:remove"/> + <xs:element ref="Sk:replace"/> + <xs:element ref="Sk:roundRect"/> + <xs:element ref="Sk:save"/> + <xs:element ref="Sk:snapshot"/> + <xs:element ref="Sk:string"/> + <xs:element ref="Sk:text"/> + <xs:element ref="Sk:textBox"/> + <xs:element ref="Sk:textOnPath"/> + <xs:element ref="Sk:textToPath"/> + </xs:choice> + <!-- @attribute condition If present and zero, the contained elements are ignored + when drawn. --> + <xs:attribute name="condition" type="Sk:DynamicString"/> + <!-- @attribute enableCondition If present and zero, the contained elements are ignored + when enabled. --> + <xs:attribute name="enableCondition" type="Sk:DynamicString"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <xs:element name="hitClear" > + <xs:complexType> + <xs:choice maxOccurs="1"> + <xs:element ref="Sk:array"/> + </xs:choice> + <!-- @attribute targets An array of element IDs to clear their hit-tested state. --> + <xs:attribute name="targets" type="Sk:DisplayableArray"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <xs:element name="hitTest" > + <xs:complexType> + <xs:choice maxOccurs="2"> + <xs:element ref="Sk:array"/> + </xs:choice> + <!-- @attribute bullets An array of element IDs to test for intersection with targets. --> + <xs:attribute name="bullets" type="Sk:DisplayableArray"/> + <!-- @attribute hits The targets the bullets hit. A read-only array of indices, one index + per bullet. The value of the array element is the index of the target hit, or -1 if no + target was hit. --> + <xs:attribute name="hits" type="Sk:IntArray"/> + <!-- @attribute targets An array of element IDs to test for intersection with bullets. --> + <xs:attribute name="targets" type="Sk:DisplayableArray"/> + <!-- @attribute value Read only; set to true if some bullet hit some target. --> + <xs:attribute name="value" type="Sk:Boolean"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** image + Image creates a reference to a JPEG, PNG or GIF. The image may be referenced + through the local file system, the internet, or embedded in the document in Base64 + format. The specific image type is determined by examining the byte stream. + */ --> + <xs:element name="image"> + <xs:complexType> + <!-- @attribute base64 The image in Base64 notation. See http://rfc.net/rfc2045.html + for the base64 format. --> + <xs:attribute name="base64" type="Sk:Base64"/> + <!-- @attribute height The height of the image (read-only). --> + <xs:attribute name="height" type="Sk:Int"/> + <!-- @attribute src The URI reference, local to the contaiing document. --> + <xs:attribute name="src" type="Sk:String"/> + <!-- @attribute width The width of the image (read-only). --> + <xs:attribute name="width" type="Sk:Int"/> + <!-- @attribute x The position of the left edge of the image in local coordinates. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The position of the top edge of the image in local coordinates. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** include + Include adds the referenced XML to the containing document. Unlike movie, the XML + directives can reference the document's IDs and can define new IDs that are referenced + by the remainder of the document or subsequent includes. + */ --> + <xs:element name="include"> + <xs:complexType> + <!-- @attribute src The URI reference, local to the containing document, + containing the include's XML. --> + <xs:attribute name="src" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** input + Input captures the metadata passed from an event. When the metadata's name or id + matches the metadata's name, the metadata's payload is copied to the corresponding + input attribute. + */ --> + <xs:element name="input"> + <xs:complexType> + <!-- @attribute float The floating point payload carried by the metadata. --> + <xs:attribute name="float" type="Sk:Float"/> + <!-- @attribute initialized A read-only value set to true if the input received a value + from the event. --> + <xs:attribute name="initialized" type="Sk:Boolean"/> + <!-- @attribute int The signed integer payload carried by the metadata. --> + <xs:attribute name="int" type="Sk:Int"/> + <!-- @attribute name The name of the metadata containing the payload. Note that + the name or id may match the payload, but that XML requires the id to be + uniquely defined in the document, while multiple input elements may reuse + the name. --> + <xs:attribute name="name" type="Sk:String"/> + <!-- @attribute string The text payload carried by the metadata. --> + <xs:attribute name="string" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** int + Int contains an integer. The int element cannot be added to a display list, but can + by set by animations and read by any attribute definition. An int element may be used, + for instance, to index through an array element. + */ --> + <xs:element name="int"> + <xs:complexType> + <!-- @attribute value The contained integer. --> + <xs:attribute name="value" type="Sk:Int"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** line + Line describes a line between two points. As noted below, the paint's stroke and + strokeAndFill attributes are ignored. + */ --> + <xs:element name="line"> + <xs:complexType> + <!-- @attribute x1 The start point's x value. --> + <xs:attribute name="x1" type="Sk:Float"/> + <!-- @attribute x2 The stop point's x value. --> + <xs:attribute name="x2" type="Sk:Float"/> + <!-- @attribute y1 The start point's y value. --> + <xs:attribute name="y1" type="Sk:Float"/> + <!-- @attribute y2 The stop point's y value. --> + <xs:attribute name="y2" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** lineTo + LineTo adds a line from the last point in a path to the specified point. + */ --> + <xs:element name="lineTo"> + <xs:complexType> + <!-- @attribute x The final path x coordinate. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The final path y coordinate. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** linearGradient + LinearGradient sets the paint shader to ramp between two or more colors. + */ --> + <xs:element name="linearGradient"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <xs:element ref="Sk:color"/> + <xs:element ref="Sk:matrix"/> + </xs:choice> + <!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. --> + <xs:attribute name="matrix" type="Sk:Matrix"/> + <!-- @attribute tileMode One of @pattern. @patternDescription --> + <xs:attribute name="tileMode" type="Sk:TileMode"/> + <!-- @attribute offsets An optional array of values used to bias the colors. The first entry + in the array must be 0.0, the last must be 1.0, and intermediate values must ascend. --> + <xs:attribute name="offsets" type="Sk:FloatArray"/> + <!-- @attribute points Two points describing the start and end of the gradient. --> + <xs:attribute name="points" type="Sk:Point"/> <!-- not right; should be array of 2 points --> + <!-- @attribute unitMapper A script that returns the mapping for [0,1] for the gradient. + The script can use the predefined variable 'unit' to compute the mapping. For instance, + "unit*unit" squares the value (while still keeping it in the range of [0,1].) The computed number + is pinned to from 0 to 1 after the script is executed. --> + <xs:attribute name="unitMapper" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** maskFilter + MaskFilter disables any mask filter referenced by the paint. + */ --> + <xs:element name="maskFilter"> + <xs:complexType> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** matrix + Matrix transforms all points drawn to the canvas. The matrix may translate, scale, skew, rotate, + or apply perspective, or apply any combination. + */ --> + <xs:element name="matrix"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <!-- @element fromPath FromPath maps a unit vector to a position and direction on a path. --> + <xs:element ref="Sk:fromPath"/> + <!-- @element polyToPoly PolyToPoly maps a points between two polygons. --> + <xs:element ref="Sk:polyToPoly"/> + <!-- @element rectToRect RectToRect maps a points between two rectangles. --> + <xs:element ref="Sk:rectToRect"/> + <!-- @element rotate Rotate computes the matrix rotation in degrees. --> + <xs:element ref="Sk:rotate"/> + <!-- @element scale Scale stretches or shrinks horizontally, vertically, or both. --> + <xs:element ref="Sk:scale"/> + <!-- @element skew Skew slants horizontally, vertically, or both. --> + <xs:element ref="Sk:skew"/> + <!-- @element translate Translate moves horizontally, vertically, or both. --> + <xs:element ref="Sk:translate"/> + </xs:choice> + <!-- @attribute matrix Nine floats describing a 3x3 matrix. --> + <xs:attribute name="matrix" type="Sk:FloatArray"/> + <!-- @attribute perspectX The [0][2] element of the 3x3 matrix. --> + <xs:attribute name="perspectX" type="Sk:Float"/> + <!-- @attribute perspectY The [1][2] element of the 3x3 matrix. --> + <xs:attribute name="perspectY" type="Sk:Float"/> + <!-- @attribute rotate The angle to rotate in degrees. --> + <xs:attribute name="rotate" type="Sk:Float"/> + <!-- @attribute scale The scale to apply in both X and Y.. --> + <xs:attribute name="scale" type="Sk:Float"/> + <!-- @attribute scaleX The [0][0] element of the 3x3 matrix. --> + <xs:attribute name="scaleX" type="Sk:Float"/> + <!-- @attribute scaleY The [1][1] element of the 3x3 matrix. --> + <xs:attribute name="scaleY" type="Sk:Float"/> + <!-- @attribute skewX The [0][1] element of the 3x3 matrix. --> + <xs:attribute name="skewX" type="Sk:Float"/> + <!-- @attribute skewY The [1][0] element of the 3x3 matrix. --> + <xs:attribute name="skewY" type="Sk:Float"/> + <!-- @attribute translate A point specifying the translation in X and Y. --> + <xs:attribute name="translate" type="Sk:Point"/> + <!-- @attribute translateX The [2][0] element of the 3x3 matrix. --> + <xs:attribute name="translateX" type="Sk:Float"/> + <!-- @attribute translateY The [2][1] element of the 3x3 matrix. --> + <xs:attribute name="translateY" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** move + Move an element in the display list in front or behind other elements. + If where and offset are omitted, the element is moved to the end of the display list. + If where is specified, the element is moved before the first occurance of where in the display list. + If offset and where are specified, the element is moved before where plus offset. + A positive offset without where moves the element to the start of the list plus offset. + A negative offset without where moves the element to the end of the list minus offset. + */ --> + <xs:element name="move"> + <xs:complexType> + <!-- @attribute mode Has no effect. --> + <xs:attribute name="mode" type="Sk:AddMode"/> + <!-- @attribute offset The destination position using the rules listed above. --> + <xs:attribute name="offset" type="Sk:Int"/> + <!-- @attribute use The element to move. --> + <xs:attribute name="use" type="Sk:Drawable"/> + <!-- @attribute where The ID of the first display list entry to move to. --> + <xs:attribute name="where" type="Sk:Drawable"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** moveTo + MoveTo specifies the first point in a path contour. + */ --> + <xs:element name="moveTo"> + <xs:complexType> + <!-- @attribute x The point's x coordinate. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The point's y coordinate. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** movie + Movie describes a display list within the current canvas and paint. Movies can contain + movies. One movie cannot affect how another movie draws, but movies can communicate + with each other by posting events. + */ --> + <xs:element name="movie"> + <xs:complexType> + <!-- @attribute src The URI reference, local to the containing document, containing the movie's XML. --> + <xs:attribute name="src" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** oval + Oval describes a circle stretched to fit in a rectangle. + The width and height attribute compute the oval's right and bottom edges when the oval + description is first seen. Animating the oval's left or top will not recompute the right or bottom + if the width or height have been specified. + */ --> + <xs:element name="oval"> + <xs:complexType> + <!-- @attribute bottom The bottom edge of the oval. --> + <xs:attribute name="bottom" type="Sk:Float"/> + <!-- @attribute height The height of the oval. --> + <xs:attribute name="height" type="Sk:Float"/> + <!-- @attribute left The left edge of the oval. --> + <xs:attribute name="left" type="Sk:Float"/> + <!-- @attribute needsRedraw Set to true if last draw was visible. --> + <xs:attribute name="needsRedraw" type="Sk:Boolean"/> + <!-- @attribute right The right edge of the oval. --> + <xs:attribute name="right" type="Sk:Float"/> + <!-- @attribute top The top edge of the oval. --> + <xs:attribute name="top" type="Sk:Float"/> + <!-- @attribute width The width of the oval. --> + <xs:attribute name="width" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** paint + Paint uses color, flags, path effects, mask filters, shaders, and stroke effects when drawing + geometries, images, and text. + */ --> + <xs:element name="paint"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <!-- @element bitmapShader Sets or cancels an image to draw as the color. --> + <xs:element ref="Sk:bitmapShader"/> + <!-- @element blur Blur radially draws the shape with varying transparency. --> + <xs:element ref="Sk:blur"/> + <!-- @element color Color specifies a solid color in RGB or HSV. --> + <xs:element ref="Sk:color"/> + <!-- @element dash Dashes alternates stroking with dashes and gaps. --> + <xs:element ref="Sk:dash"/> + <!-- @element discrete Discrete wobbles the geometry randomly. --> + <xs:element ref="Sk:discrete"/> + <!-- @element emboss Emboss simulates a 3D light to show highlights and relief. --> + <xs:element ref="Sk:emboss"/> + <!-- @element linearGradient LinearGradient linearly ramps between two or more colors. --> + <xs:element ref="Sk:linearGradient"/> + <!-- @element maskFilter MaskFilter cancels a blur or emboss. --> + <xs:element ref="Sk:maskFilter"/> + <!-- @element pathEffect PathEffect cancels a discrete or dash. --> + <xs:element ref="Sk:pathEffect"/> + <!-- @element radialGradient RadialGradient radially ramps between two or more colors. --> + <xs:element ref="Sk:radialGradient"/> + <!-- @element shader Shader cancels a linear or radial gradient. --> + <xs:element ref="Sk:shader"/> + <!-- @element typeface Typeface chooses a font out of a font family. --> + <xs:element ref="Sk:typeface"/> + <!-- @element transparentShader TransparentShader ? [not sure what this is for] --> + <xs:element ref="Sk:transparentShader"/> + </xs:choice> + <!-- @attribute antiAlias AntiAlias uses gray shades to increase the definition of paths. --> + <xs:attribute name="antiAlias" type="Sk:Boolean"/> + <!-- @attribute ascent Ascent returns the height above the baseline defined by the font. --> + <xs:attribute name="ascent" type="Sk:Float"/> + <!-- @attribute color Color sets the paint to the color element with this ID. --> + <xs:attribute name="color" type="Sk:Color"/> + <!-- @attribute descent Descent returns the height below the baseline defined by thte font --> + <xs:attribute name="descent" type="Sk:Float"/> + <!-- @attribute fakeBold FakeBold enables a faked bold for text. --> + <xs:attribute name="fakeBold" type="Sk:Boolean"/> + <!-- @attribute filterType FilterType --> + <xs:attribute name="filterType" type="Sk:FilterType"/> + <!-- @attribute linearText LinearText uses the ideal path metrics at all sizes to describe text. --> + <xs:attribute name="linearText" type="Sk:Boolean"/> + <!-- @attribute maskFilter MaskFilter specifies a blur or emboss with this ID. --> + <xs:attribute name="maskFilter" type="Sk:MaskFilter"/> + <!-- @attribute measureText MeasureText(String) returns the width of the string in this paint. --> + <xs:attribute name="measureText" type="Sk:Float"/> + <!-- @attribute pathEffect PathEffect specifies a discrete or dash with this ID. --> + <xs:attribute name="pathEffect" type="Sk:PathEffect"/> + <!-- @attribute shader Shader specifies a gradient with this ID. --> + <xs:attribute name="shader" type="Sk:Shader"/> + <!-- @attribute strikeThru StrikeThru adds a line through the middle of drawn text. --> + <xs:attribute name="strikeThru" type="Sk:Boolean"/> + <!-- @attribute stroke Stroke draws the outline of geometry according to the pen attributes. + If style is also present, its setting overrides stroke. --> + <xs:attribute name="stroke" type="Sk:Boolean"/> + <!-- @attribute strokeCap StrokeCap is one of @pattern. --> + <xs:attribute name="strokeCap" type="Sk:Cap"/> + <!-- @attribute strokeJoin StrokeJoin is one of @pattern. --> + <xs:attribute name="strokeJoin" type="Sk:Join"/> + <!-- @attribute strokeMiter StrokeMiter limits the pen's joins on narrow angles. --> + <xs:attribute name="strokeMiter" type="Sk:Float"/> + <!-- @attribute strokeWidth StrokeWidth specifies the width of the pen. --> + <xs:attribute name="strokeWidth" type="Sk:Float"/> + <!-- @attribute style Style fills, strokes, or strokes and fills the geometry with the paint's color. --> + <xs:attribute name="style" type="Sk:Style"/> + <!-- @attribute textAlign TextAlign is one of @pattern. --> + <xs:attribute name="textAlign" type="Sk:Align"/> + <!-- @attribute textScaleX TextScaleX condenses or exapnds the text. --> + <xs:attribute name="textScaleX" type="Sk:Float"/> + <!-- @attribute textSize TextSize specifies the point size of the text. --> + <xs:attribute name="textSize" type="Sk:Float"/> + <!-- @attribute textSkewX TextSkewX draws the text obliquely. --> + <xs:attribute name="textSkewX" type="Sk:Float"/> + <!-- @attribute textTracking TextTracking specifies the space between letters. --> + <xs:attribute name="textTracking" type="Sk:Float"/> + <!-- @attribute typeface Typeface specifies a typeface element with this ID. --> + <xs:attribute name="typeface" type="Sk:Typeface"/> + <!-- @attribute underline Underline draws a line under the baseline of the text. --> + <xs:attribute name="underline" type="Sk:Boolean"/> + <!-- @attribute xfermode Xfermode specifies a transfer mode, one of @pattern. --> + <xs:attribute name="xfermode" type="Sk:Xfermode"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** path + Path creates a geometry out of lines and curves. + */ --> + <xs:element name="path"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <!-- @element addCircle Adds a circle to the path. --> + <xs:element ref="Sk:addCircle"/> + <!-- @element addOval Adds an oval to the path. --> + <xs:element ref="Sk:addOval"/> + <!-- @element addPath Adds another path to the path. --> + <xs:element ref="Sk:addPath"/> + <!-- @element addRoundRect Adds a rounded-corner rectangle to the path. --> + <xs:element ref="Sk:addRoundRect"/> + <!-- @element close Connects the last point on the path to the first. --> + <xs:element ref="Sk:close"/> + <!-- @element cubicTo Extends the path with a cubic curve. --> + <xs:element ref="Sk:cubicTo"/> + <!-- @element lineTo Extends the path with a line. --> + <xs:element ref="Sk:lineTo"/> + <!-- @element moveTo Starts a new path contour. --> + <xs:element ref="Sk:moveTo"/> + <!-- @element quadTo Extends the path with a quadratic curve. --> + <xs:element ref="Sk:quadTo"/> + <!-- @element rCubicTo Extends the path with a cubic curve expressed with relative offsets. --> + <xs:element ref="Sk:rCubicTo"/> + <!-- @element rLineTo Extends the path with a line expressed with relative offsets. --> + <xs:element ref="Sk:rLineTo"/> + <!-- @element rMoveTo Starts a new path contour relative to the path's last point. --> + <xs:element ref="Sk:rMoveTo"/> + <!-- @element rQuadTo Extends the path with a quadratic curve expressed with relative offsets. --> + <xs:element ref="Sk:rQuadTo"/> + </xs:choice> + <!-- @attribute d Creates a path using SVG path notation. --> + <xs:attribute name="d" type="Sk:String"/> + <!-- @attribute fillType One of @pattern. --> + <xs:attribute name="fillType" type="Sk:FillType"/> + <!-- @attribute length Returns the length of the path. --> + <xs:attribute name="length" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** pathEffect + PathEffect cancels any current path effect within the paint, such as dashing or discrete. + */ --> + <xs:element name="pathEffect"> + <xs:complexType> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** point + Point describes a two dimensional point in space. The point element can be added + to the display list and drawn. + */ --> + <xs:element name="point"> + <xs:complexType> + <!-- @attribute x The x coordinate of the point. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The y coordinate of the point. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** polygon + Polygon creates a geometry out of lines. Polygon is a specialization of path; element that + refers to a path can refer to a polygon also. A polygon specified through elements behaves identically + to a path. A polygon specified by the points attribute contains a single contour, and the contour is + automatically closed. + */ --> + <xs:element name="polygon"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <!-- @element close Connects the last point on the path to the first. --> + <xs:element ref="Sk:close"/> + <!-- @element addPath Adds another path to the path. --> + <xs:element ref="Sk:addPath"/> + <!-- @element lineTo Extends the path with a line. --> + <xs:element ref="Sk:lineTo"/> + <!-- @element moveTo Starts a new path contour. --> + <xs:element ref="Sk:moveTo"/> + <!-- @element rLineTo Extends the path with a line expressed with relative offsets. --> + <xs:element ref="Sk:rLineTo"/> + <!-- @element rMoveTo Starts a new path contour relative to the path's last point. --> + <xs:element ref="Sk:rMoveTo"/> + </xs:choice> + <!-- @attribute points An array of values that describe a sequence of points, compatible with SVG. --> + <xs:attribute name="points" type="Sk:FloatArray"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** polyline + Polyline creates a geometry out of lines. Polygon is a specialization of path; element that + refers to a path can refer to a polygon also. A polygon specified through elements behaves identically + to a path. A polygon specified by the points attribute contains a single contour, and the contour is + not automatically closed. + */ --> + <xs:element name="polyline"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <!-- @element close Connects the last point on the path to the first. --> + <xs:element ref="Sk:close"/> + <!-- @element addPath Adds another path to the path. --> + <xs:element ref="Sk:addPath"/> + <!-- @element lineTo Extends the path with a line. --> + <xs:element ref="Sk:lineTo"/> + <!-- @element moveTo Starts a new path contour. --> + <xs:element ref="Sk:moveTo"/> + <!-- @element rLineTo Extends the path with a line expressed with relative offsets. --> + <xs:element ref="Sk:rLineTo"/> + <!-- @element rMoveTo Starts a new path contour relative to the path's last point. --> + <xs:element ref="Sk:rMoveTo"/> + </xs:choice> + <!-- @attribute points An array of values that describe a sequence of points, compatible with SVG. --> + <xs:attribute name="points" type="Sk:FloatArray"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** polyToPoly + PolyToPoly creates a matrix which maps points proportionally from one polygon to the other. + */ --> + <xs:element name="polyToPoly"> + <xs:complexType> + <xs:choice maxOccurs="2"> + <xs:element ref="Sk:polygon"/> + </xs:choice> + <!-- @attribute source The polygon to map from.. --> + <xs:attribute name="source" type="Sk:polygon"/> + <!-- @attribute destination The polygon to map to.. --> + <xs:attribute name="destination" type="Sk:polygon"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** post + Post activates an event. The event can trigger one or more actions, and can carry a data payload. + */ --> + <xs:element name="post"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <xs:element ref="Sk:data"/> + </xs:choice> + <!-- @attribute delay Time in seconds that must elapse before the target event is activated. --> + <xs:attribute name="delay" type="Sk:MSec"/> + <!-- @attribute mode One of @pattern. @patternDescription --> + <xs:attribute name="mode" type="Sk:EventMode"/> + <!-- @attribute sink The optional named EventSink to direct the event to. --> + <xs:attribute name="sink" type="Sk:String"/> + <!-- @attribute target The ID of the user event to trigger. --> + <xs:attribute name="target" type="Sk:String"/> + <!-- @attribute type The name of the external event to post. --> + <xs:attribute name="type" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** quadTo + QuadTo adds a quadratic curve to a path. + */ --> + <xs:element name="quadTo"> + <xs:complexType> + <!-- @attribute x1 The x position of the off-curve point. --> + <xs:attribute name="x1" type="Sk:Float"/> + <!-- @attribute x2 The x position of the final point. --> + <xs:attribute name="x2" type="Sk:Float"/> + <!-- @attribute y1 The y position of the off-curve point. --> + <xs:attribute name="y1" type="Sk:Float"/> + <!-- @attribute y2 The y position of the final point. --> + <xs:attribute name="y2" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** rCubicTo + RCubicTo adds a cubic to the path, using the last point in the path as the first point of the cubic. THe + added points are offsets from the last point in the path. + */ --> + <xs:element name="rCubicTo"> + <xs:complexType> + <!-- @attribute x1 The x offset of the first off-curve point. --> + <xs:attribute name="x1" type="Sk:Float"/> + <!-- @attribute x2 The x offset of the second off-curve point. --> + <xs:attribute name="x2" type="Sk:Float"/> + <!-- @attribute x3 The x offset of the final on-curve point. --> + <xs:attribute name="x3" type="Sk:Float"/> + <!-- @attribute y1 The y offset of the first off-curve point. --> + <xs:attribute name="y1" type="Sk:Float"/> + <!-- @attribute y2 The y offset of the second off-curve point. --> + <xs:attribute name="y2" type="Sk:Float"/> + <!-- @attribute y3 The y offset of the final on-curve point. --> + <xs:attribute name="y3" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** rLineTo + RLineTo adds a line from the last point in a path to the specified point. The specified + point is relative to the last point in the path. + */ --> + <xs:element name="rLineTo"> + <xs:complexType> + <!-- @attribute x The final path x coordinate. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The final path y coordinate. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** rMoveTo + RMoveTo specifies the first point in a path contour. The specified + point is relative to the last point in the path. + */ --> + <xs:element name="rMoveTo"> + <xs:complexType> + <!-- @attribute x The point's x coordinate. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The point's y coordinate. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** rQuadTo + RQuadTo adds a quadratic curve to a path. The quadratic + points are relative to the last point in the path. + */ --> + <xs:element name="rQuadTo"> + <xs:complexType> + <!-- @attribute x1 The x position of the off-curve point. --> + <xs:attribute name="x1" type="Sk:Float"/> + <!-- @attribute x2 The x position of the final point. --> + <xs:attribute name="x2" type="Sk:Float"/> + <!-- @attribute y1 The y position of the off-curve point. --> + <xs:attribute name="y1" type="Sk:Float"/> + <!-- @attribute y2 The y position of the final point. --> + <xs:attribute name="y2" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** radialGradient + RadialGradient sets the paint shader to ramp between two or more colors in concentric circles. + */ --> + <xs:element name="radialGradient"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <xs:element ref="Sk:color"/> + <xs:element ref="Sk:matrix"/> + </xs:choice> + <!-- @attribute matrix Matrix applies a 3x3 transform to the gradient. --> + <xs:attribute name="matrix" type="Sk:Matrix"/> + <!-- @attribute tileMode One of @pattern. @patternDescription --> + <xs:attribute name="tileMode" type="Sk:TileMode"/> + <!-- @attribute center The center point of the radial gradient. --> + <xs:attribute name="center" type="Sk:Point"/> + <!-- @attribute offsets An optional array of values used to bias the colors. The first entry + in the array must be 0.0, the last must be 1.0, and intermediate values must ascend. --> + <xs:attribute name="offsets" type="Sk:FloatArray"/> + <!-- @attribute radius The distance from the first color to the last color. --> + <xs:attribute name="radius" type="Sk:Float"/> + <!-- @attribute unitMapper A script that returns the mapping for [0,1] for the gradient. + The script can use the predefined variable 'unit' to compute the mapping. For instance, + "unit*unit" squares the value (while still keeping it in the range of [0,1].) The computed number + is pinned to from 0 to 1 after the script is executed. --> + <xs:attribute name="unitMapper" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** random + Random generates a random number, from min to max. Each time the random attribute is + read, a new random number is generated. + */ --> + <xs:element name="random"> + <xs:complexType> + <!-- @attribute blend The random bias from 0.0 to 1.0. + 0.0 biias the number towards the start and end of the range. + 1.0 (the default) generates a linear distribution.--> + <xs:attribute name="blend" type="Sk:Float"/> + <!-- @attribute max The largest value to generate. --> + <xs:attribute name="max" type="Sk:Float"/> + <!-- @attribute min The smallest value to generate. --> + <xs:attribute name="min" type="Sk:Float"/> + <!-- @attribute random The generated value. --> + <xs:attribute name="random" type="Sk:Float"/> + <!-- @attribute seed The random seed. Identical seeds generate the same series of + numbers. --> + <xs:attribute name="seed" type="Sk:Int"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** rect + Rect describes a bounding box. + The width and height attribute compute the rectangle's right and bottom edges when the rectangle + description is first seen. Animating the rectangle's left or top will not recompute the right or bottom + if the width or height have been specified. + */ --> + <xs:element name="rect"> + <xs:complexType> + <!-- @attribute bottom The bottom edge of the rectangle. --> + <xs:attribute name="bottom" type="Sk:Float"/> + <!-- @attribute height The height of the rectangle. Setting height computes the + bottom attribute from the top attribute. --> + <xs:attribute name="height" type="Sk:Float"/> + <!-- @attribute left The left edge of the rectangle. --> + <xs:attribute name="left" type="Sk:Float"/> + <!-- @attribute needsRedraw Set to true if last draw was visible. --> + <xs:attribute name="needsRedraw" type="Sk:Boolean"/> + <!-- @attribute right The right edge of the rectangle. --> + <xs:attribute name="right" type="Sk:Float"/> + <!-- @attribute top The top edge of the rectangle. --> + <xs:attribute name="top" type="Sk:Float"/> + <!-- @attribute width The width of the rectangle. --> + <xs:attribute name="width" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** rectToRect + RectToRect adds a matrix to map one rectangle's coordinates to another. + */ --> + <xs:element name="rectToRect"> + <xs:complexType> + <xs:choice maxOccurs="2"> + <xs:element ref="Sk:rect"/> + </xs:choice> + <!-- @attribute source The rectangle to map from. --> + <xs:attribute name="source" type="Sk:rect"/> + <!-- @attribute destination The rectangle to map to. --> + <xs:attribute name="destination" type="Sk:rect"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** remove + Remove an item from the display list. + If where is specified, the first occurance of where in the display list is removed. + If offset and where are specified, the element at where plus offset is removed. + A positive offset without where removes the element at the start of the list plus offset. + A negative offset without where removes the element at the end of the list minus offset. + */ --> + <xs:element name="remove"> + <xs:complexType> + <!-- @attribute delete If true, reverse the action of apply's attribute mode="create". + (Experimental.) --> + <xs:attribute name="delete" type="Sk:Boolean"/> + <!-- @attribute offset The destination position using the rules listed above. --> + <xs:attribute name="offset" type="Sk:Int"/> + <!-- @attribute where The ID of the first display list entry to remove. --> + <xs:attribute name="where" type="Sk:Drawable"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** replace + Replace an item in the display list. + If where is specified, the first occurance of where in the display list is replaced by use. + If offset and where are specified, the element at where plus offset is replaced by use. + A positive offset without where replaces the element at the start of the list plus offset. + A negative offset without where replaces the element at the end of the list minus offset. + */ --> + <xs:element name="replace"> + <xs:complexType> + <!-- @attribute mode Has no effect. --> + <xs:attribute name="mode" type="Sk:AddMode"/> + <!-- @attribute offset The destination position using the rules listed above. --> + <xs:attribute name="offset" type="Sk:Int"/> + <!-- @attribute use The element to be added to the display list.. --> + <xs:attribute name="use" type="Sk:Drawable"/> + <!-- @attribute where The ID of the first display list entry to remove. --> + <xs:attribute name="where" type="Sk:Drawable"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** rotate + Rotate creates a matrix that rotates a unit vector about a center point, and concatenated + with the containing matrix. + */ --> + <xs:element name="rotate"> + <xs:complexType> + <!-- @attribute center A point the rotation is centered about; by default, [0.0, 0.0]. --> + <xs:attribute name="center" type="Sk:Point"/> + <!-- @attribute degrees The rotation in degrees. --> + <xs:attribute name="degrees" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** roundRect + RoundRect creates a rectangle with rounded corners. The rounded corners are specified by + two axes, which describe an quarter-section of the oval which is used in each corner. + The width and height attribute compute the rectangle's right and bottom edges when the rectangle + description is first seen. Animating the rectangle's left or top will not recompute the right or bottom + if the width or height have been specified. + */ --> + <xs:element name="roundRect"> + <xs:complexType> + <!-- @attribute bottom The bottom edge of the rectangle. --> + <xs:attribute name="bottom" type="Sk:Float"/> + <!-- @attribute height The height of the rectangle. Setting height computes the + bottom attribute from the top attribute. --> + <xs:attribute name="height" type="Sk:Float"/> + <!-- @attribute left The left edge of the rectangle. --> + <xs:attribute name="left" type="Sk:Float"/> + <!-- @attribute needsRedraw Set to true if last draw was visible. --> + <xs:attribute name="needsRedraw" type="Sk:Boolean"/> + <!-- @attribute right The right edge of the rectangle. --> + <xs:attribute name="right" type="Sk:Float"/> + <!-- @attribute top The top edge of the rectangle. --> + <xs:attribute name="top" type="Sk:Float"/> + <!-- @attribute rx The radius of the corners on the x axis. --> + <xs:attribute name="rx" type="Sk:Float"/> + <!-- @attribute ry The radius of the corners on the y axis. --> + <xs:attribute name="ry" type="Sk:Float"/> + <!-- @attribute width The width of the rectangle. Setting width computes the + right attribute from the left attribute. --> + <xs:attribute name="width" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** save + The save element collects a series of elements into a group. The state of the paint and + canvas are saved, so that edits to the paint and canvas within the group are restored + to their original value at the end of the group. + The save element can be referenced + or defined within elements, like apply, which operate on any kind of element. Groups + may contain groups. + */ --> + <xs:element name="save"> + <xs:complexType> + <xs:choice maxOccurs="unbounded"> + <xs:element ref="Sk:add"/> + <xs:element ref="Sk:apply"/> + <xs:element ref="Sk:array"/> + <xs:element ref="Sk:bitmap"/> + <xs:element ref="Sk:boolean"/> + <xs:element ref="Sk:bounds"/> + <!-- <xs:element ref="Sk3D:camera"/> --> + <xs:element ref="Sk:clear"/> + <xs:element ref="Sk:clip"/> + <xs:element ref="Sk:color"/> + <xs:element ref="Sk:drawTo"/> + <xs:element ref="Sk:float"/> + <xs:element ref="Sk:full"/> + <xs:element ref="Sk:group"/> + <xs:element ref="Sk:hitClear"/> + <xs:element ref="Sk:hitTest"/> + <xs:element ref="Sk:image"/> + <xs:element ref="Sk:int"/> + <xs:element ref="Sk:line"/> + <xs:element ref="Sk:matrix"/> + <xs:element ref="Sk:move"/> + <xs:element ref="Sk:oval"/> + <xs:element ref="Sk:paint"/> + <!-- <xs:element ref="Sk:patch"/> --> + <xs:element ref="Sk:path"/> + <xs:element ref="Sk:point"/> + <xs:element ref="Sk:polygon"/> + <xs:element ref="Sk:polyline"/> + <xs:element ref="Sk:post"/> + <xs:element ref="Sk:random"/> + <xs:element ref="Sk:rect"/> + <xs:element ref="Sk:remove"/> + <xs:element ref="Sk:replace"/> + <xs:element ref="Sk:roundRect"/> + <xs:element ref="Sk:save"/> + <xs:element ref="Sk:set"/> + <xs:element ref="Sk:snapshot"/> + <xs:element ref="Sk:string"/> + <xs:element ref="Sk:text"/> + <xs:element ref="Sk:textBox"/> + <xs:element ref="Sk:textOnPath"/> + <xs:element ref="Sk:textToPath"/> + </xs:choice> + <!-- @attribute condition If present and zero, the contained elements are ignored. --> + <xs:attribute name="condition" type="Sk:DynamicString"/> + <!-- @attribute enableCondition If present and zero, the contained elements are ignored + when enabled. --> + <xs:attribute name="enableCondition" type="Sk:DynamicString"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** scale + Scale creates a matrix that scales a unit vector about a center point, and concatenated + with the containing matrix. + */ --> + <xs:element name="scale"> + <xs:complexType> + <!-- @attribute center A point the scale is centered about; by default, [0.0, 0.0]. --> + <xs:attribute name="center" type="Sk:Point"/> + <!-- @attribute x The factor all x values are scaled by; by default, 1.0. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The factor all y values are scaled by; by default, 1.0. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** screenplay + Screenplay contains all events and elements referenced by the events. + A document may only contain a single screenplay element. + */ --> + <xs:element name="screenplay"> + <xs:complexType> + <xs:choice maxOccurs="unbounded" > + <xs:element ref="Sk:add"/> + <xs:element ref="Sk:apply"/> + <xs:element ref="Sk:array"/> + <xs:element ref="Sk:bitmap"/> + <xs:element ref="Sk:boolean"/> + <xs:element ref="Sk:bounds"/> + <!-- <xs:element ref="Sk3D:camera"/> --> + <xs:element ref="Sk:clear"/> + <xs:element ref="Sk:clip"/> + <xs:element ref="Sk:color"/> + <xs:element ref="Sk:drawTo"/> + <xs:element ref="Sk:event"/> + <xs:element ref="Sk:float"/> + <xs:element ref="Sk:full"/> + <xs:element ref="Sk:group"/> + <xs:element ref="Sk:hitClear"/> + <xs:element ref="Sk:hitTest"/> + <xs:element ref="Sk:image"/> + <xs:element ref="Sk:include"/> + <xs:element ref="Sk:int"/> + <xs:element ref="Sk:line"/> + <xs:element ref="Sk:matrix"/> + <xs:element ref="Sk:move"/> + <xs:element ref="Sk:movie"/> + <xs:element ref="Sk:oval"/> + <xs:element ref="Sk:paint"/> + <!-- <xs:element ref="Sk:patch"/> --> + <xs:element ref="Sk:path"/> + <xs:element ref="Sk:point"/> + <xs:element ref="Sk:polygon"/> + <xs:element ref="Sk:polyline"/> + <xs:element ref="Sk:post"/> + <xs:element ref="Sk:random"/> + <xs:element ref="Sk:rect"/> + <xs:element ref="Sk:remove"/> + <xs:element ref="Sk:replace"/> + <xs:element ref="Sk:roundRect"/> + <xs:element ref="Sk:save"/> + <xs:element ref="Sk:set"/> + <xs:element ref="Sk:snapshot"/> + <xs:element ref="Sk:string"/> + <xs:element ref="Sk:text"/> + <xs:element ref="Sk:textBox"/> + <xs:element ref="Sk:textOnPath"/> + <xs:element ref="Sk:textToPath"/> + </xs:choice> + <!-- @attribute time The time of the draw (readable from script; not part of the document XML) --> + <xs:attribute name="time" type="Sk:MSec"/> + </xs:complexType> + </xs:element> + + <!-- /** set + Set animates the target element's attribute directly to the specified value. + */ --> + <xs:element name="set"> + <xs:complexType> + <!-- @attribute begin An optional offset that must elapse before the animation begins. The apply + begin attribute is added to any animator's begin attribute. --> + <xs:attribute name="begin" type="Sk:MSec"/> + <!-- @attribute dur The duration of the animation in milliseconds. --> + <xs:attribute name="dur" type="Sk:MSec"/> + <!-- @attribute dynamic If true, restart the animation if any of the simple values the + 'lval' or 'to' attributes reference are changed. Simple values are contained by the array, boolean, float, int, + and string elements. --> + <!-- @attribute dynamic [Depreciated.] --> + <xs:attribute name="dynamic" type="Sk:Boolean" /> + <!-- @attribute field The attribute to animate. --> + <xs:attribute name="field" type="Sk:String"/> + <!-- @attribute formula A script to execute over time to compute the field's value. Typically, + the fomula is a script expression which includes a reference to the time attribute of the + containing apply element. --> + <xs:attribute name="formula" type="Sk:DynamicString"/> + <!-- @attribute lval An expression evaluating to the attribute to animate. + If present, lval overrides 'field'. The expression is typically an array element, + e.g. lval="x[y]" . --> + <xs:attribute name="lval" type="Sk:DynamicString"/> + <!-- @attribute reset If true, the computed value is the initial value after the + animation is complete. If false, or by default, the computed value is the final value + after the animation is complete. --> + <xs:attribute name="reset" type="Sk:Boolean"/> + <!-- @attribute step When apply's attribute mode="immediate" or "create", the step attribute can be read by + script to determine the current animation iteration. --> + <xs:attribute name="step" type="Sk:Int" /> + <!-- @attribute target The element to animate. By default, the element contained by the apply + or referenced by the apply's scope attribute is the animate target. --> + <xs:attribute name="target" type="Sk:DynamicString"/> + <!-- @attribute to The ending value (requires a 'from' attribute) --> + <xs:attribute name="to" type="Sk:DynamicString"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** skew + Skew creates a matrix that skews a unit vector about a center point, and concatenated + with the containing matrix. + */ --> + <xs:element name="skew"> + <xs:complexType> + <!-- @attribute center A point the skew is centered about; by default, [0.0, 0.0]. --> + <xs:attribute name="center" type="Sk:Point"/> + <!-- @attribute x The factor all x values are skewed by; by default, 0.0. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The factor all y values are skewed by; by default, 0.0. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** snapshot + Snapshot creates an image file containing the display list. + */ --> + <xs:element name="snapshot"> + <xs:complexType> + <!-- @attribute filename The name of the file to generate. --> + <xs:attribute name="filename" type="Sk:String"/> + <!-- @attribute quality The quality of the image, from 0 to 100. --> + <xs:attribute name="quality" type="Sk:Float"/> + <!-- @attribute sequence Set to true to number the filenames sequentially. --> + <xs:attribute name="sequence" type="Sk:Boolean"/> + <!-- @attribute type One of @pattern. The type of encoding to use. --> + <xs:attribute name="type" type="Sk:BitmapEncoding"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** string + String contains an array of characters. + */ --> + <xs:element name="string" > + <xs:complexType> + <!-- @attribute length The number of characters in the string (read only). --> + <xs:attribute name="length" type="Sk:Int"/> + <!-- @attribute slice An ECMAScript compatible function that returns part of the string. --> + <xs:attribute name="slice" type="Sk:String"/> + <!-- @attribute value The string itself. --> + <xs:attribute name="value" type="Sk:String"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** text + A drawable string with a position. + */ --> + <xs:element name="text"> + <xs:complexType> + <!-- @attribute length The number of characters in the string (read only). --> + <xs:attribute name="length" type="Sk:Int"/> + <!-- @attribute text The string itself. --> + <xs:attribute name="text" type="Sk:String"/> + <!-- @attribute x The x coordinate of the string. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The y coordinate of the string. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** textBox + A drawable string fit into a box. + */ --> + <xs:element name="textBox" > + <xs:complexType> + <!-- @attribute bottom The bottom of the box. --> + <xs:attribute name="bottom" type="Sk:Float"/> + <!-- @attribute height The height of the box, computed from top and bottom. --> + <xs:attribute name="height" type="Sk:Float"/> + <!-- @attribute left The left side of the box. --> + <xs:attribute name="left" type="Sk:Float"/> + <!-- @attribute mode One of @pattern. --> + <xs:attribute name="mode" type="Sk:TextBoxMode"/> + <!-- @attribute needsRedraw Set to true if last draw was visible. --> + <xs:attribute name="needsRedraw" type="Sk:Boolean"/> + <!-- @attribute right The right side of the box. --> + <xs:attribute name="right" type="Sk:Float"/> + <!-- @attribute spacingAdd The extra spacing between lines. --> + <xs:attribute name="spacingAdd" type="Sk:Float"/> + <!-- @attribute spacingAlign One of @pattern. --> + <xs:attribute name="spacingAlign" type="Sk:TextBoxAlign"/> + <!-- @attribute spacingMul The line spacing scaled by the text height. --> + <xs:attribute name="spacingMul" type="Sk:Float"/> + <!-- @attribute text The text to fit to the box. --> + <xs:attribute name="text" type="Sk:String"/> + <!-- @attribute top The top of the box. --> + <xs:attribute name="top" type="Sk:Float"/> + <!-- @attribute width The width of the box, computed from left and right. --> + <xs:attribute name="width" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** textOnPath + TextOnPath specifies the baseline for a string of text with a path. + */ --> + <xs:element name="textOnPath"> + <xs:complexType> + <xs:choice > + <xs:element ref="Sk:text" minOccurs="0" /> + <xs:element ref="Sk:path" minOccurs="0" /> + </xs:choice> + <!-- @attribute offset The distance along the path to place the first text character. --> + <xs:attribute name="offset" type="Sk:Float"/> + <!-- @attribute path The baseline of the text. --> + <xs:attribute name="path" type="Sk:Path"/> + <!-- @attribute text The text to place along the path. --> + <xs:attribute name="text" type="Sk:Text"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** textToPath + TextToPath sets the path to the contours described by the text's glyphs, using the current paint. + */ --> + <xs:element name="textToPath"> + <xs:complexType> + <xs:choice > + <xs:element ref="Sk:text" minOccurs="0" /> + <xs:element ref="Sk:paint" minOccurs="0" /> + <xs:element ref="Sk:path" minOccurs="0" /> + </xs:choice> + <!-- @attribute paint The paint selects the text font, size and other text properties. --> + <xs:attribute name="paint" type="Sk:Paint"/> + <!-- @attribute path The reference to the path element where the text as path is stored. --> + <xs:attribute name="path" type="Sk:Path"/> + <!-- @attribute text The reference to the text element to turn into a path. --> + <xs:attribute name="text" type="Sk:Text"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** translate + Translate concatenates a translation-only matrix onto the current matrix. + */ --> + <xs:element name="translate"> + <xs:complexType> + <!-- @attribute x The translation in x. --> + <xs:attribute name="x" type="Sk:Float"/> + <!-- @attribute y The translation in y. --> + <xs:attribute name="y" type="Sk:Float"/> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** transparentShader + TransparentShader uses the background for its paint. Works well with emboss. + */ --> + <xs:element name="transparentShader"> + <xs:complexType> + <xs:attribute name="id" type="xs:ID"/> + </xs:complexType> + </xs:element> + + <!-- /** typeface + Typeface describes the text font. + */ --> + <xs:element name="typeface"> + <xs:complexType> + <!-- @attribute fontName The name of the font. --> + <xs:attribute name="fontName" type="Sk:String"/> + </xs:complexType> + </xs:element> + +</xs:schema> + diff --git a/src/animator/SkAnimateSchema.xsx b/src/animator/SkAnimateSchema.xsx new file mode 100644 index 0000000..ceb7d89 --- /dev/null +++ b/src/animator/SkAnimateSchema.xsx @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--This file is auto-generated by the XML Schema Designer. It holds layout information for components on the designer surface.-->
+<XSDDesignerLayout /> diff --git a/src/animator/SkDrawExtraPathEffect.cpp b/src/animator/SkDrawExtraPathEffect.cpp index 4cca738..76b55c6 100644 --- a/src/animator/SkDrawExtraPathEffect.cpp +++ b/src/animator/SkDrawExtraPathEffect.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -70,7 +70,7 @@ class SkDrawComposePathEffect : public SkDrawPathEffect { virtual ~SkDrawComposePathEffect(); virtual bool add(SkAnimateMaker& , SkDisplayable* ); virtual SkPathEffect* getPathEffect(); - virtual bool isPaint() const; + virtual bool isPaint() const; private: SkDrawPathEffect* effect1; SkDrawPathEffect* effect2; @@ -127,7 +127,7 @@ protected: m.reset(); if (fDraw->addMatrix) { SkDrawMatrix* matrix; - if (fDraw->addMatrix->getType() == SkType_Matrix) + if (fDraw->addMatrix->getType() == SkType_Matrix) matrix = (SkDrawMatrix*) fDraw->addMatrix; else { SkApply* apply = (SkApply*) fDraw->addMatrix; @@ -185,12 +185,12 @@ const SkMemberInfo SkDrawShapePathEffect::fInfo[] = { DEFINE_GET_MEMBER(SkDrawShapePathEffect); -SkDrawShapePathEffect::SkDrawShapePathEffect() : +SkDrawShapePathEffect::SkDrawShapePathEffect() : addPath(NULL), addMatrix(NULL), path(NULL), fPathEffect(NULL) { } SkDrawShapePathEffect::~SkDrawShapePathEffect() { - fPathEffect->safeUnref(); + SkSafeUnref(fPathEffect); } bool SkDrawShapePathEffect::add(SkAnimateMaker& , SkDisplayable* child) { @@ -234,7 +234,7 @@ void SkDrawShape1DPathEffect::onEndElement(SkAnimateMaker& maker) { class SkShape2DPathEffect : public Sk2DPathEffect { public: - SkShape2DPathEffect(SkDrawShape2DPathEffect* draw, SkAnimateMaker* maker, + SkShape2DPathEffect(SkDrawShape2DPathEffect* draw, SkAnimateMaker* maker, const SkMatrix& matrix) : Sk2DPathEffect(matrix), fDraw(draw), fMaker(maker) { } @@ -265,7 +265,7 @@ protected: goto clearCallBack; if (fDraw->matrix) { SkDrawMatrix* matrix; - if (fDraw->matrix->getType() == SkType_Matrix) + if (fDraw->matrix->getType() == SkType_Matrix) matrix = (SkDrawMatrix*) fDraw->matrix; else { SkApply* apply = (SkApply*) fDraw->matrix; @@ -301,7 +301,7 @@ private: } return true; } - + SkPoint fLoc; SkRect fUVBounds; int32_t fU; @@ -418,13 +418,13 @@ const char kDrawCornerPathEffectName[] = "pathEffect:corner"; class SkExtraPathEffects : public SkExtras { public: - SkExtraPathEffects(SkAnimator* animator) : + SkExtraPathEffects(SkAnimator* animator) : skDrawShape1DPathEffectType(SkType_Unknown), skDrawShape2DPathEffectType(SkType_Unknown), skDrawComposePathEffectType(SkType_Unknown), skDrawCornerPathEffectType(SkType_Unknown) { } - + virtual SkDisplayable* createInstance(SkDisplayTypes type) { SkDisplayable* result = NULL; if (skDrawShape1DPathEffectType == type) @@ -439,8 +439,8 @@ public: } virtual bool definesType(SkDisplayTypes type) { - return type == skDrawShape1DPathEffectType || - type == skDrawShape2DPathEffectType || + return type == skDrawShape1DPathEffectType || + type == skDrawShape2DPathEffectType || type == skDrawComposePathEffectType || type == skDrawCornerPathEffectType; } diff --git a/src/animator/SkDrawPaint.cpp b/src/animator/SkDrawPaint.cpp index 0c23771..f21a15a 100644 --- a/src/animator/SkDrawPaint.cpp +++ b/src/animator/SkDrawPaint.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -77,11 +77,11 @@ DEFINE_GET_MEMBER(SkDrawPaint); SkDrawPaint::SkDrawPaint() : antiAlias(-1), color(NULL), fakeBold(-1), filterBitmap(-1), linearText(-1), maskFilter((SkDrawMaskFilter*) -1), pathEffect((SkDrawPathEffect*) -1), shader((SkDrawShader*) -1), strikeThru(-1), stroke(-1), - strokeCap((SkPaint::Cap) -1), strokeJoin((SkPaint::Join) -1), strokeMiter(SK_ScalarNaN), + strokeCap((SkPaint::Cap) -1), strokeJoin((SkPaint::Join) -1), strokeMiter(SK_ScalarNaN), strokeWidth(SK_ScalarNaN), style((SkPaint::Style) -1), - textAlign((SkPaint::Align) -1), textScaleX(SK_ScalarNaN), textSize(SK_ScalarNaN), + textAlign((SkPaint::Align) -1), textScaleX(SK_ScalarNaN), textSize(SK_ScalarNaN), textSkewX(SK_ScalarNaN), typeface((SkDrawTypeface*) -1), - underline(-1), xfermode((SkXfermode::Mode) -1), fOwnsColor(false), fOwnsMaskFilter(false), + underline(-1), xfermode((SkXfermode::Mode) -1), fOwnsColor(false), fOwnsMaskFilter(false), fOwnsPathEffect(false), fOwnsShader(false), fOwnsTypeface(false) { } @@ -102,7 +102,7 @@ bool SkDrawPaint::add(SkAnimateMaker& maker, SkDisplayable* child) { SkASSERT(child && child->isPaintPart()); SkPaintPart* part = (SkPaintPart*) child; if (part->add()) - maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPaint); + maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPaint); return true; } @@ -154,8 +154,8 @@ void SkDrawPaint::dump(SkAnimateMaker* maker) { dumpChildren(maker, closedYet); } #endif - -void SkDrawPaint::executeFunction(SkDisplayable* target, int index, + +void SkDrawPaint::executeFunction(SkDisplayable* target, int index, SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type, SkScriptValue* scriptValue) { if (scriptValue == NULL) @@ -169,9 +169,9 @@ void SkDrawPaint::executeFunction(SkDisplayable* target, int index, setupPaint(&paint); scriptValue->fType = SkType_Float; SkASSERT(parameters[0].fType == SkType_String); - scriptValue->fOperand.fScalar = paint.measureText(parameters[0].fOperand.fString->c_str(), - parameters[0].fOperand.fString->size()); -// SkDebugf("measureText: %s = %g\n", parameters[0].fOperand.fString->c_str(), + scriptValue->fOperand.fScalar = paint.measureText(parameters[0].fOperand.fString->c_str(), + parameters[0].fOperand.fString->size()); +// SkDebugf("measureText: %s = %g\n", parameters[0].fOperand.fString->c_str(), // scriptValue->fOperand.fScalar / 65536.0f); } break; default: @@ -195,7 +195,7 @@ bool SkDrawPaint::getProperty(int index, SkScriptValue* value) const { case SK_PROPERTY(descent): value->fOperand.fScalar = metrics.fDescent; break; - // should consider returning fLeading as well (or roll it into ascent/descent somehow + // should consider returning fLeading as well (or roll it into ascent/descent somehow default: SkASSERT(0); return false; @@ -217,7 +217,7 @@ bool SkDrawPaint::resolveIDs(SkAnimateMaker& maker, SkDisplayable* origDisp, SkA return true; if (fOwnsTypeface && maker.resolveID(typeface, original->typeface) == false) return true; - return false; // succeeded + return false; // succeeded } void SkDrawPaint::setupPaint(SkPaint* paint) const { @@ -239,15 +239,15 @@ void SkDrawPaint::setupPaint(SkPaint* paint) const { if (maskFilter == NULL) paint->setMaskFilter(NULL); else if (maskFilter != (SkDrawMaskFilter*) -1) - paint->setMaskFilter(maskFilter->getMaskFilter())->safeUnref(); + SkSafeUnref(paint->setMaskFilter(maskFilter->getMaskFilter())); if (pathEffect == NULL) paint->setPathEffect(NULL); else if (pathEffect != (SkDrawPathEffect*) -1) - paint->setPathEffect(pathEffect->getPathEffect())->safeUnref(); + SkSafeUnref(paint->setPathEffect(pathEffect->getPathEffect())); if (shader == NULL) paint->setShader(NULL); else if (shader != (SkDrawShader*) -1) - paint->setShader(shader->getShader())->safeUnref(); + SkSafeUnref(paint->setShader(shader->getShader())); if (strikeThru != -1) paint->setStrikeThruText(SkToBool(strikeThru)); if (strokeCap != (SkPaint::Cap) -1) @@ -269,7 +269,7 @@ void SkDrawPaint::setupPaint(SkPaint* paint) const { if (typeface == NULL) paint->setTypeface(NULL); else if (typeface != (SkDrawTypeface*) -1) - paint->setTypeface(typeface->getTypeface())->safeUnref(); + SkSafeUnref(paint->setTypeface(typeface->getTypeface())); if (underline != -1) paint->setUnderlineText(SkToBool(underline)); if (xfermode != (SkXfermode::Mode) -1) diff --git a/src/animator/SkExtraPathEffects.xsd b/src/animator/SkExtraPathEffects.xsd new file mode 100644 index 0000000..9592443 --- /dev/null +++ b/src/animator/SkExtraPathEffects.xsd @@ -0,0 +1,33 @@ +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+xmlns:Sk="urn:screenplay"
+xmlns:extra="urn:extraPathEffects" targetNamespace="urn:extraPathEffects" >
+ <xs:import namespace="urn:screenplay"
+ schemaLocation="SkAnimateSchema.xsd" />
+
+ <xs:element name="composePathEffect" >
+ <xs:complexType>
+ <xs:choice maxOccurs="1">
+ <xs:element ref="Sk:dash"/>
+ <xs:element ref="extra:shape1DPathEffect"/>
+ </xs:choice>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="shape1DPathEffect" >
+ <xs:complexType>
+ <xs:choice maxOccurs="1">
+ <xs:element ref="Sk:matrix"/>
+ <xs:element ref="Sk:path"/>
+ </xs:choice>
+ <xs:attribute name="addPath" type="Sk:DynamicString" />
+ <xs:attribute name="matrix" type="Sk:DynamicString" />
+ <xs:attribute name="path" type="Sk:Path" />
+ <xs:attribute name="phase" type="Sk:DynamicString"/>
+ <xs:attribute name="spacing" type="Sk:DynamicString"/>
+ <xs:attribute name="id" type="xs:ID"/>
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
+ diff --git a/src/animator/thingstodo.txt b/src/animator/thingstodo.txt new file mode 100644 index 0000000..8d0d47a --- /dev/null +++ b/src/animator/thingstodo.txt @@ -0,0 +1,21 @@ +things to do: + figure out where endless or very deep recursion is possible + at these points, generate an error if actual physical stack gets too large + candidates are scripts + eval(eval(eval... user callouts + ((((( operator precedence or similar making stack deep + groups within groups + very large apply create or apply immediate steps + + write tests for math functions + looks like random takes a parameter when it should take zero parameters + + add Math, Number files to perforce for docs + alphabetize attributes in docs + + manually modified tools/screenplayDocs/xmlToJPEG.cpp + + fix docs where lines are stitched together (insert space) + + naked <data> outside of <post> asserts on name + handle errors for all element not contained by correct parents
\ No newline at end of file diff --git a/src/core/SkAdvancedTypefaceMetrics.cpp b/src/core/SkAdvancedTypefaceMetrics.cpp new file mode 100644 index 0000000..0c5ad8d --- /dev/null +++ b/src/core/SkAdvancedTypefaceMetrics.cpp @@ -0,0 +1,158 @@ +/* + * 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 "SkAdvancedTypefaceMetrics.h" +#include "SkTypes.h" + +#ifdef SK_BUILD_FOR_UNIX +#include <ft2build.h> +#include FT_FREETYPE_H +#endif + +namespace skia_advanced_typeface_metrics_utils { + +template <typename Data> +void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range, + int startId) { + range->fStartId = startId; + range->fAdvance.setCount(0); +} + +template <typename Data> +SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* appendRange( + SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> >* nextSlot, + int startId) { + nextSlot->reset(new SkAdvancedTypefaceMetrics::AdvanceMetric<Data>); + resetRange(nextSlot->get(), startId); + return nextSlot->get(); +} + +template <typename Data> +void finishRange( + SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range, + int endId, + typename SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::MetricType + type) { + range->fEndId = endId; + range->fType = type; + int newLength; + if (type == SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange) { + newLength = endId - range->fStartId + 1; + } else { + newLength = 1; + } + SkASSERT(range->fAdvance.count() >= newLength); + range->fAdvance.setCount(newLength); +} + +template <typename Data, typename FontHandle> +SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData( + FontHandle fontHandle, + int num_glyphs, + bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data)) { + // Assuming that an ASCII representation of a width or a glyph id is, + // on average, 3 characters long gives the following cut offs for + // using different range types: + // When currently in a range + // - Removing 4 0's is a win + // - Removing 5 repeats is a win + // When not currently in a range + // - Removing 1 0 is a win + // - Removing 3 repeats is a win + + SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result; + SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange; + curRange = appendRange(&result, 0); + Data lastAdvance = SK_MinS16; + int repeats = 0; + for (int gId = 0; gId < num_glyphs; gId++) { + Data advance; + if (!getAdvance(fontHandle, gId, &advance)) { + num_glyphs = (gId > 0) ? gId - 1 : 0; + break; + } + if (advance == lastAdvance) { + repeats++; + } else if (curRange->fAdvance.count() == repeats + 1) { + if (lastAdvance == 0 && repeats >= 0) { + resetRange(curRange, gId); + } else if (repeats >= 2) { + finishRange(curRange, gId - 1, + SkAdvancedTypefaceMetrics::WidthRange::kRun); + curRange = appendRange(&curRange->fNext, gId); + } + repeats = 0; + } else { + if (lastAdvance == 0 && repeats >= 3) { + finishRange(curRange, gId - repeats - 2, + SkAdvancedTypefaceMetrics::WidthRange::kRange); + curRange = appendRange(&curRange->fNext, gId); + } else if (repeats >= 4) { + finishRange(curRange, gId - repeats - 2, + SkAdvancedTypefaceMetrics::WidthRange::kRange); + curRange = appendRange(&curRange->fNext, gId - repeats - 1); + curRange->fAdvance.append(1, &lastAdvance); + finishRange(curRange, gId - 1, + SkAdvancedTypefaceMetrics::WidthRange::kRun); + curRange = appendRange(&curRange->fNext, gId); + } + repeats = 0; + } + curRange->fAdvance.append(1, &advance); + lastAdvance = advance; + } + finishRange(curRange, num_glyphs - 1, + SkAdvancedTypefaceMetrics::WidthRange::kRange); + return result.release(); +} + +// Make AdvanceMetric template functions available for linking with typename +// WidthRange and VerticalAdvanceRange. +#if defined(SK_BUILD_FOR_WIN) +template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( + HDC hdc, + int num_glyphs, + bool (*getAdvance)(HDC hdc, int gId, int16_t* data)); +#elif defined(SK_BUILD_FOR_UNIX) +template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( + FT_Face face, + int num_glyphs, + bool (*getAdvance)(FT_Face face, int gId, int16_t* data)); +#endif +template void resetRange( + SkAdvancedTypefaceMetrics::WidthRange* range, + int startId); +template SkAdvancedTypefaceMetrics::WidthRange* appendRange( + SkTScopedPtr<SkAdvancedTypefaceMetrics::WidthRange >* nextSlot, + int startId); +template void finishRange<int16_t>( + SkAdvancedTypefaceMetrics::WidthRange* range, + int endId, + SkAdvancedTypefaceMetrics::WidthRange::MetricType type); + +template void resetRange( + SkAdvancedTypefaceMetrics::VerticalAdvanceRange* range, + int startId); +template SkAdvancedTypefaceMetrics::VerticalAdvanceRange* appendRange( + SkTScopedPtr<SkAdvancedTypefaceMetrics::VerticalAdvanceRange >* + nextSlot, + int startId); +template void finishRange<SkAdvancedTypefaceMetrics::VerticalMetric>( + SkAdvancedTypefaceMetrics::VerticalAdvanceRange* range, + int endId, + SkAdvancedTypefaceMetrics::VerticalAdvanceRange::MetricType type); + +} // namespace skia_advanced_typeface_metrics_utils diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp index e7d9537..4db7377 100644 --- a/src/core/SkBitmap.cpp +++ b/src/core/SkBitmap.cpp @@ -116,7 +116,7 @@ SkBitmap& SkBitmap::operator=(const SkBitmap& src) { */ if (NULL == fPixelRef) { // leave fPixels as it is - fColorTable->safeRef(); // ref the user's ctable if present + SkSafeRef(fColorTable); // ref the user's ctable if present } else { // we have a pixelref, so pixels/ctable reflect it // ignore the values from the memcpy fPixels = NULL; @@ -225,6 +225,30 @@ size_t SkBitmap::ComputeSize(Config c, int width, int height) { return isPos32Bits(size) ? size.get32() : 0; } +Sk64 SkBitmap::ComputeSafeSize64(Config config, + uint32_t width, + uint32_t height, + uint32_t rowBytes) { + Sk64 safeSize; + safeSize.setZero(); + if (height > 0) { + safeSize.set(ComputeRowBytes(config, width)); + Sk64 sizeAllButLastRow; + sizeAllButLastRow.setMul(height - 1, rowBytes); + safeSize.add(sizeAllButLastRow); + } + SkASSERT(!safeSize.isNeg()); + return safeSize; +} + +size_t SkBitmap::ComputeSafeSize(Config config, + uint32_t width, + uint32_t height, + uint32_t rowBytes) { + Sk64 safeSize = ComputeSafeSize64(config, width, height, rowBytes); + return (safeSize.is32() ? safeSize.get32() : 0); +} + void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) { this->freePixels(); @@ -287,7 +311,7 @@ SkPixelRef* SkBitmap::setPixelRef(SkPixelRef* pr, size_t offset) { this->freePixels(); SkASSERT(NULL == fPixelRef); - pr->safeRef(); + SkSafeRef(pr); fPixelRef = pr; } fPixelRefOffset = offset; @@ -371,57 +395,11 @@ void SkBitmap::notifyPixelsChanged() const { } } -/////////////////////////////////////////////////////////////////////////////// - -SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size, - SkColorTable* ctable) { - SkASSERT(storage); - fStorage = storage; - fSize = size; - fCTable = ctable; - ctable->safeRef(); -} - -SkMallocPixelRef::~SkMallocPixelRef() { - SkSafeUnref(fCTable); - sk_free(fStorage); +SkGpuTexture* SkBitmap::getTexture() const { + return fPixelRef ? fPixelRef->getTexture() : NULL; } -void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) { - *ct = fCTable; - return fStorage; -} - -void SkMallocPixelRef::onUnlockPixels() { - // nothing to do -} - -void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - - buffer.write32(fSize); - buffer.writePad(fStorage, fSize); - if (fCTable) { - buffer.writeBool(true); - fCTable->flatten(buffer); - } else { - buffer.writeBool(false); - } -} - -SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) : INHERITED(buffer, NULL) { - fSize = buffer.readU32(); - fStorage = sk_malloc_throw(fSize); - buffer.read(fStorage, fSize); - if (buffer.readBool()) { - fCTable = SkNEW_ARGS(SkColorTable, (buffer)); - } else { - fCTable = NULL; - } -} - -static SkPixelRef::Registrar reg("SkMallocPixelRef", - SkMallocPixelRef::Create); +/////////////////////////////////////////////////////////////////////////////// /** We explicitly use the same allocator for our pixels that SkMask does, so that we can freely assign memory allocated by one class to the other. @@ -446,6 +424,100 @@ bool SkBitmap::HeapAllocator::allocPixelRef(SkBitmap* dst, /////////////////////////////////////////////////////////////////////////////// +size_t SkBitmap::getSafeSize() const { + // This is intended to be a size_t version of ComputeSafeSize64(), just + // faster. The computation is meant to be identical. + return (fHeight ? ((fHeight - 1) * fRowBytes) + + ComputeRowBytes(getConfig(), fWidth): 0); +} + +Sk64 SkBitmap::getSafeSize64() const { + return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes); +} + +bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, int dstRowBytes) + const { + + if (dstRowBytes == -1) + dstRowBytes = fRowBytes; + SkASSERT(dstRowBytes >= 0); + + if (getConfig() == kRLE_Index8_Config || + dstRowBytes < ComputeRowBytes(getConfig(), fWidth) || + dst == NULL || (getPixels() == NULL && pixelRef() == NULL)) + return false; + + if (static_cast<uint32_t>(dstRowBytes) == fRowBytes) { + size_t safeSize = getSafeSize(); + if (safeSize > dstSize || safeSize == 0) + return false; + else { + SkAutoLockPixels lock(*this); + // This implementation will write bytes beyond the end of each row, + // excluding the last row, if the bitmap's stride is greater than + // strictly required by the current config. + memcpy(dst, getPixels(), safeSize); + + return true; + } + } else { + // If destination has different stride than us, then copy line by line. + if (ComputeSafeSize(getConfig(), fWidth, fHeight, dstRowBytes) > + dstSize) + return false; + else { + // Just copy what we need on each line. + uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth); + SkAutoLockPixels lock(*this); + const uint8_t* srcP = reinterpret_cast<const uint8_t*>(getPixels()); + uint8_t* dstP = reinterpret_cast<uint8_t*>(dst); + for (uint32_t row = 0; row < fHeight; + row++, srcP += fRowBytes, dstP += dstRowBytes) { + memcpy(dstP, srcP, rowBytes); + } + + return true; + } + } +} + +bool SkBitmap::copyPixelsFrom(const void* const src, size_t srcSize, + int srcRowBytes) { + + if (srcRowBytes == -1) + srcRowBytes = fRowBytes; + SkASSERT(srcRowBytes >= 0); + + size_t safeSize = getSafeSize(); + uint32_t rowBytes = ComputeRowBytes(getConfig(), fWidth); + if (getConfig() == kRLE_Index8_Config || src == NULL || + static_cast<uint32_t>(srcRowBytes) < rowBytes || + safeSize == 0 || + srcSize < ComputeSafeSize(getConfig(), fWidth, fHeight, srcRowBytes)) { + return false; + } + + SkAutoLockPixels lock(*this); + if (static_cast<uint32_t>(srcRowBytes) == fRowBytes) { + // This implementation will write bytes beyond the end of each row, + // excluding the last row, if the bitmap's stride is greater than + // strictly required by the current config. + memcpy(getPixels(), src, safeSize); + } else { + // Just copy the bytes we need on each line. + const uint8_t* srcP = reinterpret_cast<const uint8_t*>(src); + uint8_t* dstP = reinterpret_cast<uint8_t*>(getPixels()); + for (uint32_t row = 0; row < fHeight; + row++, srcP += srcRowBytes, dstP += fRowBytes) { + memcpy(dstP, srcP, rowBytes); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + bool SkBitmap::isOpaque() const { switch (fConfig) { case kNo_Config: @@ -768,7 +840,7 @@ bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const { */ if (this->config() == dstConfig) { if (tmp.getSize() == this->getSize()) { - memcpy(tmp.getPixels(), this->getPixels(), this->getSize()); + memcpy(tmp.getPixels(), this->getPixels(), this->getSafeSize()); } else { const char* srcP = reinterpret_cast<const char*>(this->getPixels()); char* dstP = reinterpret_cast<char*>(tmp.getPixels()); @@ -1063,7 +1135,7 @@ static bool GetBitmapAlpha(const SkBitmap& src, uint8_t SK_RESTRICT alpha[], } return false; } - + if (SkBitmap::kA8_Config == config && !src.isOpaque()) { const uint8_t* s = src.getAddr8(0, 0); while (--h >= 0) { @@ -1279,7 +1351,7 @@ void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) { size_t offset = buffer.readU32(); SkPixelRef::Factory fact = deserialize_factory(buffer); SkPixelRef* pr = fact(buffer); - this->setPixelRef(pr, offset)->safeUnref(); + SkSafeUnref(this->setPixelRef(pr, offset)); break; } case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE: @@ -1290,10 +1362,11 @@ void SkBitmap::unflatten(SkFlattenableReadBuffer& buffer) { } if (this->allocPixels(ctable)) { this->lockPixels(); - buffer.read(this->getPixels(), size); + buffer.read(this->getPixels(), this->getSafeSize()); // Just read what we need. + buffer.skip(size - this->getSafeSize()); // Keep aligned for subsequent reads. this->unlockPixels(); } else { - buffer.skip(size); + buffer.skip(size); // Still skip the full-sized buffer though. } SkSafeUnref(ctable); break; diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp index c3fd7d0..1eaa46d 100644 --- a/src/core/SkBitmapProcShader.cpp +++ b/src/core/SkBitmapProcShader.cpp @@ -44,8 +44,10 @@ void SkBitmapProcShader::endSession() { this->INHERITED::endSession(); } -bool SkBitmapProcShader::asABitmap(SkBitmap* texture, SkMatrix* texM, - TileMode xy[]) { +SkShader::BitmapType SkBitmapProcShader::asABitmap(SkBitmap* texture, + SkMatrix* texM, + TileMode xy[], + SkScalar* twoPointRadialParams) { if (texture) { *texture = fRawBitmap; } @@ -56,7 +58,7 @@ bool SkBitmapProcShader::asABitmap(SkBitmap* texture, SkMatrix* texM, xy[0] = (TileMode)fState.fTileModeX; xy[1] = (TileMode)fState.fTileModeY; } - return true; + return kDefault_BitmapType; } void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) { @@ -82,7 +84,7 @@ bool SkBitmapProcShader::setContext(const SkBitmap& device, fState.fOrigBitmap = fRawBitmap; fState.fOrigBitmap.lockPixels(); - if (fState.fOrigBitmap.getPixels() == NULL) { + if (!fState.fOrigBitmap.getTexture() && !fState.fOrigBitmap.readyToDraw()) { fState.fOrigBitmap.unlockPixels(); return false; } diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h index 09d53af..88598fc 100644 --- a/src/core/SkBitmapProcShader.h +++ b/src/core/SkBitmapProcShader.h @@ -32,7 +32,8 @@ public: virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count); virtual void beginSession(); virtual void endSession(); - virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*); + virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*, + SkScalar* twoPointRadialParams); static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty); diff --git a/src/core/SkBlitRow_D32.cpp b/src/core/SkBlitRow_D32.cpp index 3300ead..f500778 100644 --- a/src/core/SkBlitRow_D32.cpp +++ b/src/core/SkBlitRow_D32.cpp @@ -137,8 +137,17 @@ SkBlitRow::Proc32 SkBlitRow::Factory32(unsigned flags) { return proc; } -void SkBlitRow::Color32(SkPMColor dst[], const SkPMColor src[], int count, - SkPMColor color) { +SkBlitRow::Proc32 SkBlitRow::ColorProcFactory() { + SkBlitRow::ColorProc proc = PlatformColorProc(); + if (NULL == proc) { + proc = Color32; + } + SkASSERT(proc); + return proc; +} + +void SkBlitRow::Color32(SkPMColor dst[], const SkPMColor src[], + int count, SkPMColor color) { if (count > 0) { if (0 == color) { if (src != dst) { diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp index 930f9bf..cd3cb1b 100644 --- a/src/core/SkBlitter.cpp +++ b/src/core/SkBlitter.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -27,70 +27,62 @@ SkBlitter::~SkBlitter() {} -const SkBitmap* SkBlitter::justAnOpaqueColor(uint32_t* value) -{ +const SkBitmap* SkBlitter::justAnOpaqueColor(uint32_t* value) { return NULL; } -void SkBlitter::blitH(int x, int y, int width) -{ +void SkBlitter::blitH(int x, int y, int width) { SkASSERT(!"unimplemented"); } -void SkBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) -{ +void SkBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { SkASSERT(!"unimplemented"); } -void SkBlitter::blitV(int x, int y, int height, SkAlpha alpha) -{ - if (alpha == 255) +void SkBlitter::blitV(int x, int y, int height, SkAlpha alpha) { + if (alpha == 255) { this->blitRect(x, y, 1, height); - else - { + } else { int16_t runs[2]; runs[0] = 1; runs[1] = 0; - while (--height >= 0) + while (--height >= 0) { this->blitAntiH(x, y++, &alpha, runs); + } } } -void SkBlitter::blitRect(int x, int y, int width, int height) -{ - while (--height >= 0) +void SkBlitter::blitRect(int x, int y, int width, int height) { + while (--height >= 0) { this->blitH(x, y++, width); + } } ////////////////////////////////////////////////////////////////////////////// -static inline void bits_to_runs(SkBlitter* blitter, int x, int y, const uint8_t bits[], - U8CPU left_mask, int rowBytes, U8CPU right_mask) -{ +static inline void bits_to_runs(SkBlitter* blitter, int x, int y, + const uint8_t bits[], + U8CPU left_mask, int rowBytes, + U8CPU right_mask) { int inFill = 0; int pos = 0; - while (--rowBytes >= 0) - { + while (--rowBytes >= 0) { unsigned b = *bits++ & left_mask; - if (rowBytes == 0) + if (rowBytes == 0) { b &= right_mask; + } - for (unsigned test = 0x80; test != 0; test >>= 1) - { - if (b & test) - { - if (!inFill) - { + for (unsigned test = 0x80; test != 0; test >>= 1) { + if (b & test) { + if (!inFill) { pos = x; inFill = true; } - } - else - { - if (inFill) - { + } else { + if (inFill) { blitter->blitH(pos, y, x - pos); inFill = false; } @@ -101,16 +93,15 @@ static inline void bits_to_runs(SkBlitter* blitter, int x, int y, const uint8_t } // final cleanup - if (inFill) + if (inFill) { blitter->blitH(pos, y, x - pos); + } } -void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) -{ +void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) { SkASSERT(mask.fBounds.contains(clip)); - if (mask.fFormat == SkMask::kBW_Format) - { + if (mask.fFormat == SkMask::kBW_Format) { int cx = clip.fLeft; int cy = clip.fTop; int maskLeft = mask.fBounds.fLeft; @@ -119,17 +110,13 @@ void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) const uint8_t* bits = mask.getAddr1(cx, cy); - if (cx == maskLeft && clip.fRight == mask.fBounds.fRight) - { - while (--height >= 0) - { + if (cx == maskLeft && clip.fRight == mask.fBounds.fRight) { + while (--height >= 0) { bits_to_runs(this, cx, cy, bits, 0xFF, mask_rowBytes, 0xFF); bits += mask_rowBytes; cy += 1; } - } - else - { + } else { int left_edge = cx - maskLeft; SkASSERT(left_edge >= 0); int rite_edge = clip.fRight - maskLeft; @@ -140,42 +127,35 @@ void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) int full_runs = (rite_edge >> 3) - ((left_edge + 7) >> 3); // check for empty right mask, so we don't read off the end (or go slower than we need to) - if (rite_mask == 0) - { + if (rite_mask == 0) { SkASSERT(full_runs >= 0); full_runs -= 1; rite_mask = 0xFF; } - if (left_mask == 0xFF) + if (left_mask == 0xFF) { full_runs -= 1; + } // back up manually so we can keep in sync with our byte-aligned src // have cx reflect our actual starting x-coord cx -= left_edge & 7; - if (full_runs < 0) - { + if (full_runs < 0) { SkASSERT((left_mask & rite_mask) != 0); - while (--height >= 0) - { + while (--height >= 0) { bits_to_runs(this, cx, cy, bits, left_mask, 1, rite_mask); bits += mask_rowBytes; cy += 1; } - } - else - { - while (--height >= 0) - { + } else { + while (--height >= 0) { bits_to_runs(this, cx, cy, bits, left_mask, full_runs + 2, rite_mask); bits += mask_rowBytes; cy += 1; } } } - } - else - { + } else { int width = clip.width(); SkAutoSTMalloc<64, int16_t> runStorage(width + 1); int16_t* runs = runStorage.get(); @@ -186,8 +166,7 @@ void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) int height = clip.height(); int y = clip.fTop; - while (--height >= 0) - { + while (--height >= 0) { this->blitAntiH(clip.fLeft, y, aa, runs); aa += mask.fRowBytes; y += 1; @@ -201,9 +180,9 @@ void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) { if (clip.quickReject(mask.fBounds)) { return; } - + SkRegion::Cliperator clipper(clip, mask.fBounds); - + while (!clipper.done()) { const SkIRect& cr = clipper.rect(); this->blitMask(mask, cr); @@ -213,7 +192,7 @@ void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) { void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) { SkRegion::Cliperator clipper(clip, rect); - + while (!clipper.done()) { const SkIRect& cr = clipper.rect(); this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); @@ -223,7 +202,7 @@ void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) { void SkBlitter::blitRegion(const SkRegion& clip) { SkRegion::Iterator iter(clip); - + while (!iter.done()) { const SkIRect& cr = iter.rect(); this->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height()); @@ -231,99 +210,88 @@ void SkBlitter::blitRegion(const SkRegion& clip) { } } -/////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -void SkNullBlitter::blitH(int x, int y, int width) -{ -} +void SkNullBlitter::blitH(int x, int y, int width) {} -void SkNullBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) -{ -} +void SkNullBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) {} -void SkNullBlitter::blitV(int x, int y, int height, SkAlpha alpha) -{ -} +void SkNullBlitter::blitV(int x, int y, int height, SkAlpha alpha) {} -void SkNullBlitter::blitRect(int x, int y, int width, int height) -{ -} +void SkNullBlitter::blitRect(int x, int y, int width, int height) {} -void SkNullBlitter::blitMask(const SkMask& mask, const SkIRect& clip) -{ -} +void SkNullBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {} -const SkBitmap* SkNullBlitter::justAnOpaqueColor(uint32_t* value) -{ +const SkBitmap* SkNullBlitter::justAnOpaqueColor(uint32_t* value) { return NULL; } -/////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -static int compute_anti_width(const int16_t runs[]) -{ +static int compute_anti_width(const int16_t runs[]) { int width = 0; - - for (;;) - { + + for (;;) { int count = runs[0]; - + SkASSERT(count >= 0); - if (count == 0) + if (count == 0) { break; + } width += count; runs += count; - + SkASSERT(width < 20000); } return width; } -static inline bool y_in_rect(int y, const SkIRect& rect) -{ +static inline bool y_in_rect(int y, const SkIRect& rect) { return (unsigned)(y - rect.fTop) < (unsigned)rect.height(); } -static inline bool x_in_rect(int x, const SkIRect& rect) -{ +static inline bool x_in_rect(int x, const SkIRect& rect) { return (unsigned)(x - rect.fLeft) < (unsigned)rect.width(); } -void SkRectClipBlitter::blitH(int left, int y, int width) -{ +void SkRectClipBlitter::blitH(int left, int y, int width) { SkASSERT(width > 0); - if (!y_in_rect(y, fClipRect)) + if (!y_in_rect(y, fClipRect)) { return; + } int right = left + width; - if (left < fClipRect.fLeft) + if (left < fClipRect.fLeft) { left = fClipRect.fLeft; - if (right > fClipRect.fRight) + } + if (right > fClipRect.fRight) { right = fClipRect.fRight; + } width = right - left; - if (width > 0) + if (width > 0) { fBlitter->blitH(left, y, width); + } } -void SkRectClipBlitter::blitAntiH(int left, int y, const SkAlpha aa[], const int16_t runs[]) -{ - if (!y_in_rect(y, fClipRect) || left >= fClipRect.fRight) +void SkRectClipBlitter::blitAntiH(int left, int y, const SkAlpha aa[], + const int16_t runs[]) { + if (!y_in_rect(y, fClipRect) || left >= fClipRect.fRight) { return; + } int x0 = left; int x1 = left + compute_anti_width(runs); - if (x1 <= fClipRect.fLeft) + if (x1 <= fClipRect.fLeft) { return; + } SkASSERT(x0 < x1); - if (x0 < fClipRect.fLeft) - { + if (x0 < fClipRect.fLeft) { int dx = fClipRect.fLeft - x0; SkAlphaRuns::BreakAt((int16_t*)runs, (uint8_t*)aa, dx); runs += dx; @@ -332,8 +300,7 @@ void SkRectClipBlitter::blitAntiH(int left, int y, const SkAlpha aa[], const int } SkASSERT(x0 < x1 && runs[x1 - x0] == 0); - if (x1 > fClipRect.fRight) - { + if (x1 > fClipRect.fRight) { x1 = fClipRect.fRight; SkAlphaRuns::BreakAt((int16_t*)runs, (uint8_t*)aa, x1 - x0); ((int16_t*)runs)[x1 - x0] = 0; @@ -345,95 +312,91 @@ void SkRectClipBlitter::blitAntiH(int left, int y, const SkAlpha aa[], const int fBlitter->blitAntiH(x0, y, aa, runs); } -void SkRectClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) -{ +void SkRectClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) { SkASSERT(height > 0); - if (!x_in_rect(x, fClipRect)) + if (!x_in_rect(x, fClipRect)) { return; + } int y0 = y; int y1 = y + height; - if (y0 < fClipRect.fTop) + if (y0 < fClipRect.fTop) { y0 = fClipRect.fTop; - if (y1 > fClipRect.fBottom) + } + if (y1 > fClipRect.fBottom) { y1 = fClipRect.fBottom; + } - if (y0 < y1) + if (y0 < y1) { fBlitter->blitV(x, y0, y1 - y0, alpha); + } } -void SkRectClipBlitter::blitRect(int left, int y, int width, int height) -{ +void SkRectClipBlitter::blitRect(int left, int y, int width, int height) { SkIRect r; r.set(left, y, left + width, y + height); - if (r.intersect(fClipRect)) + if (r.intersect(fClipRect)) { fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height()); + } } -void SkRectClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) -{ +void SkRectClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) { SkASSERT(mask.fBounds.contains(clip)); SkIRect r = clip; - if (r.intersect(fClipRect)) + if (r.intersect(fClipRect)) { fBlitter->blitMask(mask, r); + } } -const SkBitmap* SkRectClipBlitter::justAnOpaqueColor(uint32_t* value) -{ +const SkBitmap* SkRectClipBlitter::justAnOpaqueColor(uint32_t* value) { return fBlitter->justAnOpaqueColor(value); } -/////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -void SkRgnClipBlitter::blitH(int x, int y, int width) -{ +void SkRgnClipBlitter::blitH(int x, int y, int width) { SkRegion::Spanerator span(*fRgn, y, x, x + width); int left, right; - while (span.next(&left, &right)) - { + while (span.next(&left, &right)) { SkASSERT(left < right); fBlitter->blitH(left, y, right - left); } } -void SkRgnClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) -{ +void SkRgnClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[], + const int16_t runs[]) { int width = compute_anti_width(runs); SkRegion::Spanerator span(*fRgn, y, x, x + width); int left, right; SkDEBUGCODE(const SkIRect& bounds = fRgn->getBounds();) - + int prevRite = x; - while (span.next(&left, &right)) - { + while (span.next(&left, &right)) { SkASSERT(x <= left); SkASSERT(left < right); SkASSERT(left >= bounds.fLeft && right <= bounds.fRight); - + SkAlphaRuns::Break((int16_t*)runs, (uint8_t*)aa, left - x, right - left); // now zero before left - if (left > prevRite) - { + if (left > prevRite) { int index = prevRite - x; ((uint8_t*)aa)[index] = 0; // skip runs after right ((int16_t*)runs)[index] = SkToS16(left - prevRite); } - + prevRite = right; } - - if (prevRite > x) - { + + if (prevRite > x) { ((int16_t*)runs)[prevRite - x] = 0; - + if (x < 0) { int skip = runs[0]; SkASSERT(skip >= -x); @@ -445,15 +408,13 @@ void SkRgnClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t } } -void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) -{ +void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) { SkIRect bounds; bounds.set(x, y, x + 1, y + height); SkRegion::Cliperator iter(*fRgn, bounds); - while (!iter.done()) - { + while (!iter.done()) { const SkIRect& r = iter.rect(); SkASSERT(bounds.contains(r)); @@ -462,15 +423,13 @@ void SkRgnClipBlitter::blitV(int x, int y, int height, SkAlpha alpha) } } -void SkRgnClipBlitter::blitRect(int x, int y, int width, int height) -{ +void SkRgnClipBlitter::blitRect(int x, int y, int width, int height) { SkIRect bounds; bounds.set(x, y, x + width, y + height); SkRegion::Cliperator iter(*fRgn, bounds); - while (!iter.done()) - { + while (!iter.done()) { const SkIRect& r = iter.rect(); SkASSERT(bounds.contains(r)); @@ -479,47 +438,38 @@ void SkRgnClipBlitter::blitRect(int x, int y, int width, int height) } } -void SkRgnClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) -{ +void SkRgnClipBlitter::blitMask(const SkMask& mask, const SkIRect& clip) { SkASSERT(mask.fBounds.contains(clip)); SkRegion::Cliperator iter(*fRgn, clip); const SkIRect& r = iter.rect(); SkBlitter* blitter = fBlitter; - while (!iter.done()) - { + while (!iter.done()) { blitter->blitMask(mask, r); iter.next(); } } -const SkBitmap* SkRgnClipBlitter::justAnOpaqueColor(uint32_t* value) -{ +const SkBitmap* SkRgnClipBlitter::justAnOpaqueColor(uint32_t* value) { return fBlitter->justAnOpaqueColor(value); } -/////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, const SkIRect* ir) -{ - if (clip) - { +SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, + const SkIRect* ir) { + if (clip) { const SkIRect& clipR = clip->getBounds(); - if (clip->isEmpty() || (ir && !SkIRect::Intersects(clipR, *ir))) + if (clip->isEmpty() || (ir && !SkIRect::Intersects(clipR, *ir))) { blitter = &fNullBlitter; - else if (clip->isRect()) - { - if (ir == NULL || !clipR.contains(*ir)) - { + } else if (clip->isRect()) { + if (ir == NULL || !clipR.contains(*ir)) { fRectBlitter.init(blitter, clipR); blitter = &fRectBlitter; } - } - else - { + } else { fRgnBlitter.init(blitter, clip); blitter = &fRgnBlitter; } @@ -527,44 +477,43 @@ SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, con return blitter; } -/////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// #include "SkColorShader.h" #include "SkColorPriv.h" class Sk3DShader : public SkShader { public: - Sk3DShader(SkShader* proxy) : fProxy(proxy) - { - proxy->safeRef(); + Sk3DShader(SkShader* proxy) : fProxy(proxy) { + SkSafeRef(proxy); fMask = NULL; } - virtual ~Sk3DShader() - { - fProxy->safeUnref(); + + virtual ~Sk3DShader() { + SkSafeUnref(fProxy); } + void setMask(const SkMask* mask) { fMask = mask; } - virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix) - { - if (fProxy) + virtual bool setContext(const SkBitmap& device, const SkPaint& paint, + const SkMatrix& matrix) { + if (fProxy) { return fProxy->setContext(device, paint, matrix); - else - { + } else { fPMColor = SkPreMultiplyColor(paint.getColor()); return this->INHERITED::setContext(device, paint, matrix); } } - virtual void shadeSpan(int x, int y, SkPMColor span[], int count) - { - if (fProxy) + + virtual void shadeSpan(int x, int y, SkPMColor span[], int count) { + if (fProxy) { fProxy->shadeSpan(x, y, span, count); + } - if (fMask == NULL) - { - if (fProxy == NULL) + if (fMask == NULL) { + if (fProxy == NULL) { sk_memset32(span, fPMColor, count); + } return; } @@ -576,15 +525,11 @@ public: const uint8_t* mulp = alpha + size; const uint8_t* addp = mulp + size; - if (fProxy) - { - for (int i = 0; i < count; i++) - { - if (alpha[i]) - { + if (fProxy) { + for (int i = 0; i < count; i++) { + if (alpha[i]) { SkPMColor c = span[i]; - if (c) - { + if (c) { unsigned a = SkGetPackedA32(c); unsigned r = SkGetPackedR32(c); unsigned g = SkGetPackedG32(c); @@ -599,73 +544,65 @@ public: span[i] = SkPackARGB32(a, r, g, b); } - } - else + } else { span[i] = 0; + } } - } - else // color - { + } else { // color unsigned a = SkGetPackedA32(fPMColor); unsigned r = SkGetPackedR32(fPMColor); unsigned g = SkGetPackedG32(fPMColor); unsigned b = SkGetPackedB32(fPMColor); - for (int i = 0; i < count; i++) - { - if (alpha[i]) - { + for (int i = 0; i < count; i++) { + if (alpha[i]) { unsigned mul = SkAlpha255To256(mulp[i]); unsigned add = addp[i]; span[i] = SkPackARGB32( a, - SkFastMin32(SkAlphaMul(r, mul) + add, a), - SkFastMin32(SkAlphaMul(g, mul) + add, a), - SkFastMin32(SkAlphaMul(b, mul) + add, a)); - } - else + SkFastMin32(SkAlphaMul(r, mul) + add, a), + SkFastMin32(SkAlphaMul(g, mul) + add, a), + SkFastMin32(SkAlphaMul(b, mul) + add, a)); + } else { span[i] = 0; + } } } } - - virtual void beginSession() - { + + virtual void beginSession() { this->INHERITED::beginSession(); - if (fProxy) + if (fProxy) { fProxy->beginSession(); + } } - - virtual void endSession() - { - if (fProxy) + + virtual void endSession() { + if (fProxy) { fProxy->endSession(); + } this->INHERITED::endSession(); } protected: Sk3DShader(SkFlattenableReadBuffer& buffer) : - INHERITED(buffer) - { + INHERITED(buffer) { fProxy = static_cast<SkShader*>(buffer.readFlattenable()); fPMColor = buffer.readU32(); fMask = NULL; } - - virtual void flatten(SkFlattenableWriteBuffer& buffer) - { + + virtual void flatten(SkFlattenableWriteBuffer& buffer) { this->INHERITED::flatten(buffer); buffer.writeFlattenable(fProxy); buffer.write32(fPMColor); } - - virtual Factory getFactory() - { + + virtual Factory getFactory() { return CreateProc; } private: - static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) - { + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return SkNEW_ARGS(Sk3DShader, (buffer)); } @@ -679,36 +616,34 @@ private: class Sk3DBlitter : public SkBlitter { public: Sk3DBlitter(SkBlitter* proxy, Sk3DShader* shader, void (*killProc)(void*)) - : fProxy(proxy), f3DShader(shader), fKillProc(killProc) - { + : fProxy(proxy), f3DShader(shader), fKillProc(killProc) { shader->ref(); } - virtual ~Sk3DBlitter() - { + + virtual ~Sk3DBlitter() { f3DShader->unref(); fKillProc(fProxy); } - virtual void blitH(int x, int y, int width) - { + virtual void blitH(int x, int y, int width) { fProxy->blitH(x, y, width); } - virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) - { + + virtual void blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { fProxy->blitAntiH(x, y, antialias, runs); } - virtual void blitV(int x, int y, int height, SkAlpha alpha) - { + + virtual void blitV(int x, int y, int height, SkAlpha alpha) { fProxy->blitV(x, y, height, alpha); } - virtual void blitRect(int x, int y, int width, int height) - { + + virtual void blitRect(int x, int y, int width, int height) { fProxy->blitRect(x, y, width, height); } - virtual void blitMask(const SkMask& mask, const SkIRect& clip) - { - if (mask.fFormat == SkMask::k3D_Format) - { + + virtual void blitMask(const SkMask& mask, const SkIRect& clip) { + if (mask.fFormat == SkMask::k3D_Format) { f3DShader->setMask(&mask); ((SkMask*)&mask)->fFormat = SkMask::kA8_Format; @@ -716,18 +651,18 @@ public: ((SkMask*)&mask)->fFormat = SkMask::k3D_Format; f3DShader->setMask(NULL); - } - else + } else { fProxy->blitMask(mask, clip); + } } + private: SkBlitter* fProxy; Sk3DShader* f3DShader; void (*fKillProc)(void*); }; -/////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// #include "SkCoreBlitters.h" @@ -739,6 +674,7 @@ public: fXfer = fPaint->getXfermode(); SkSafeRef(fXfer); } + ~SkAutoRestoreShaderXfer() { fPaint->setShader(fShader); SkSafeUnref(fShader); @@ -763,34 +699,34 @@ private: class SkAutoCallProc { public: typedef void (*Proc)(void*); + SkAutoCallProc(void* obj, Proc proc) - : fObj(obj), fProc(proc) - { - } - ~SkAutoCallProc() - { - if (fObj && fProc) + : fObj(obj), fProc(proc) {} + + ~SkAutoCallProc() { + if (fObj && fProc) { fProc(fObj); + } } + void* get() const { return fObj; } - void* detach() - { + + void* detach() { void* obj = fObj; fObj = NULL; return obj; } + private: void* fObj; Proc fProc; }; -static void destroy_blitter(void* blitter) -{ +static void destroy_blitter(void* blitter) { ((SkBlitter*)blitter)->~SkBlitter(); } -static void delete_blitter(void* blitter) -{ +static void delete_blitter(void* blitter) { SkDELETE((SkBlitter*)blitter); } @@ -804,7 +740,7 @@ static bool just_solid_color(const SkPaint& paint) { } return false; } - + /** By analyzing the paint (with an xfermode), we may decide we can take special action. This enum lists our possible actions */ @@ -817,7 +753,7 @@ enum XferInterp { static XferInterp interpret_xfermode(const SkPaint& paint, SkXfermode* xfer, SkBitmap::Config deviceConfig) { SkXfermode::Mode mode; - + if (SkXfermode::IsMode(xfer, &mode)) { switch (mode) { case SkXfermode::kSrc_Mode: @@ -855,16 +791,14 @@ static XferInterp interpret_xfermode(const SkPaint& paint, SkXfermode* xfer, SkBlitter* SkBlitter::Choose(const SkBitmap& device, const SkMatrix& matrix, const SkPaint& paint, - void* storage, size_t storageSize) -{ + void* storage, size_t storageSize) { SkASSERT(storageSize == 0 || storage != NULL); SkBlitter* blitter = NULL; // which check, in case we're being called by a client with a dummy device // (e.g. they have a bounder that always aborts the draw) - if (SkBitmap::kNo_Config == device.getConfig()) - { + if (SkBitmap::kNo_Config == device.getConfig()) { SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize); return blitter; } @@ -873,8 +807,8 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device, SkShader* shader = paint.getShader(); Sk3DShader* shader3D = NULL; - if (paint.getMaskFilter() != NULL && paint.getMaskFilter()->getFormat() == SkMask::k3D_Format) - { + if (paint.getMaskFilter() != NULL && + paint.getMaskFilter()->getFormat() == SkMask::k3D_Format) { shader3D = SkNEW_ARGS(Sk3DShader, (shader)); restorePaint.setShader(shader3D)->unref(); shader = shader3D; @@ -900,58 +834,66 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device, shader = SkNEW(SkColorShader); restorePaint.setShader(shader)->unref(); } - - if (paint.getColorFilter() != NULL) - { + + if (paint.getColorFilter() != NULL) { SkASSERT(shader); shader = SkNEW_ARGS(SkFilterShader, (shader, paint.getColorFilter())); restorePaint.setShader(shader)->unref(); // blitters should ignore the presence/absence of a filter, since // if there is one, the shader will take care of it. } - + if (shader && !shader->setContext(device, paint, matrix)) { return SkNEW(SkNullBlitter); } switch (device.getConfig()) { - case SkBitmap::kA1_Config: - SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter, storage, storageSize, (device, paint)); - break; - - case SkBitmap::kA8_Config: - if (shader) - SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter, storage, storageSize, (device, paint)); - else - SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Blitter, storage, storageSize, (device, paint)); - break; - - case SkBitmap::kARGB_4444_Config: - blitter = SkBlitter_ChooseD4444(device, paint, storage, storageSize); - break; - - case SkBitmap::kRGB_565_Config: - blitter = SkBlitter_ChooseD565(device, paint, storage, storageSize); - break; - - case SkBitmap::kARGB_8888_Config: - if (shader) - SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Shader_Blitter, storage, storageSize, (device, paint)); - else if (paint.getColor() == SK_ColorBLACK) - SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Black_Blitter, storage, storageSize, (device, paint)); - else if (paint.getAlpha() == 0xFF) - SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Opaque_Blitter, storage, storageSize, (device, paint)); - else - SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Blitter, storage, storageSize, (device, paint)); - break; - - default: - SkASSERT(!"unsupported device config"); - SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize); + case SkBitmap::kA1_Config: + SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter, + storage, storageSize, (device, paint)); + break; + + case SkBitmap::kA8_Config: + if (shader) { + SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter, + storage, storageSize, (device, paint)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Blitter, + storage, storageSize, (device, paint)); + } + break; + + case SkBitmap::kARGB_4444_Config: + blitter = SkBlitter_ChooseD4444(device, paint, storage, storageSize); + break; + + case SkBitmap::kRGB_565_Config: + blitter = SkBlitter_ChooseD565(device, paint, storage, storageSize); + break; + + case SkBitmap::kARGB_8888_Config: + if (shader) { + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Shader_Blitter, + storage, storageSize, (device, paint)); + } else if (paint.getColor() == SK_ColorBLACK) { + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Black_Blitter, + storage, storageSize, (device, paint)); + } else if (paint.getAlpha() == 0xFF) { + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Opaque_Blitter, + storage, storageSize, (device, paint)); + } else { + SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Blitter, + storage, storageSize, (device, paint)); + } + break; + + default: + SkASSERT(!"unsupported device config"); + SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize); + break; } - if (shader3D) - { + if (shader3D) { void (*proc)(void*) = ((void*)storage == (void*)blitter) ? destroy_blitter : delete_blitter; SkAutoCallProc tmp(blitter, proc); @@ -961,12 +903,12 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device, return blitter; } -////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// const uint16_t gMask_0F0F = 0xF0F; const uint32_t gMask_00FF00FF = 0xFF00FF; -////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// SkShaderBlitter::SkShaderBlitter(const SkBitmap& device, const SkPaint& paint) : INHERITED(device) { diff --git a/src/core/SkBlitter_4444.cpp b/src/core/SkBlitter_4444.cpp index 81bbc48..e1c519a 100644 --- a/src/core/SkBlitter_4444.cpp +++ b/src/core/SkBlitter_4444.cpp @@ -2,16 +2,16 @@ ** ** 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 + ** 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 + ** 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 + ** 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. */ @@ -23,13 +23,13 @@ #include "SkUtils.h" #include "SkXfermode.h" -inline SkPMColor SkBlendARGB4444(SkPMColor16 src, SkPMColor16 dst, U8CPU aa) -{ +static inline SkPMColor SkBlendARGB4444(SkPMColor16 src, SkPMColor16 dst, + U8CPU aa) { SkASSERT((unsigned)aa <= 255); - + unsigned src_scale = SkAlpha255To256(aa) >> 4; unsigned dst_scale = SkAlpha15To16(15 - SkAlphaMul4(SkGetPackedA4444(src), src_scale)); - + uint32_t src32 = SkExpand_4444(src) * src_scale; uint32_t dst32 = SkExpand_4444(dst) * dst_scale; return SkCompact_4444((src32 + dst32) >> 4); @@ -46,16 +46,16 @@ public: virtual void blitRect(int x, int y, int width, int height); virtual void blitMask(const SkMask&, const SkIRect&); virtual const SkBitmap* justAnOpaqueColor(uint32_t*); - + protected: SkPMColor16 fPMColor16, fPMColor16Other; SkPMColor16 fRawColor16, fRawColor16Other; uint8_t fScale16; - + private: // illegal SkARGB4444_Blitter& operator=(const SkARGB4444_Blitter&); - + typedef SkRasterBlitter INHERITED; }; @@ -81,7 +81,7 @@ SkARGB4444_Blitter::SkARGB4444_Blitter(const SkBitmap& device, const SkPaint& pa } else { fRawColor16Other = fRawColor16; } - + #if 0 /// don't think this assertion is true, but need it be? // our dithered color will be the same or more opaque than the original @@ -146,19 +146,19 @@ static void src_over_4444x(SkPMColor16 dst[], uint32_t color, void SkARGB4444_Blitter::blitH(int x, int y, int width) { SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); - + if (0 == fScale16) { return; } - + SkPMColor16* device = fDevice.getAddr16(x, y); SkPMColor16 color = fPMColor16; SkPMColor16 other = fPMColor16Other; - + if ((x ^ y) & 1) { SkTSwap<SkPMColor16>(color, other); } - + if (16 == fScale16) { sk_dither_memset16(device, color, other, width); } @@ -174,12 +174,12 @@ void SkARGB4444_Blitter::blitV(int x, int y, int height, SkAlpha alpha) if (0 == alpha || 0 == fScale16) { return; } - + SkPMColor16* device = fDevice.getAddr16(x, y); SkPMColor16 color = fPMColor16; SkPMColor16 other = fPMColor16Other; unsigned rb = fDevice.rowBytes(); - + if ((x ^ y) & 1) { SkTSwap<SkPMColor16>(color, other); } @@ -211,19 +211,19 @@ void SkARGB4444_Blitter::blitV(int x, int y, int height, SkAlpha alpha) void SkARGB4444_Blitter::blitRect(int x, int y, int width, int height) { SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width() && y + height <= fDevice.height()); - + if (0 == fScale16) { return; } - + SkPMColor16* device = fDevice.getAddr16(x, y); SkPMColor16 color = fPMColor16; SkPMColor16 other = fPMColor16Other; - + if ((x ^ y) & 1) { SkTSwap<SkPMColor16>(color, other); } - + if (16 == fScale16) { while (--height >= 0) { sk_dither_memset16(device, color, other, width); @@ -248,22 +248,22 @@ void SkARGB4444_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], cons if (0 == fScale16) { return; } - + SkPMColor16* device = fDevice.getAddr16(x, y); SkPMColor16 color = fPMColor16; SkPMColor16 other = fPMColor16Other; - + if ((x ^ y) & 1) { SkTSwap<SkPMColor16>(color, other); } - + for (;;) { int count = runs[0]; SkASSERT(count >= 0); if (count <= 0) { return; } - + unsigned aa = antialias[0]; if (aa) { if (0xFF == aa) { @@ -338,11 +338,11 @@ if (mask & 0x01) { dst[7] = sc + SkAlphaMulQ4(dst[7], dst_scale); } \ void SkARGB4444_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { SkASSERT(mask.fBounds.contains(clip)); - + if (0 == fScale16) { return; } - + if (mask.fFormat == SkMask::kBW_Format) { if (16 == fScale16) { SkARGB4444_BlitBW(fDevice, mask, clip, fPMColor16); @@ -351,18 +351,18 @@ void SkARGB4444_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) } return; } - + int x = clip.fLeft; int y = clip.fTop; int width = clip.width(); int height = clip.height(); - + SkPMColor16* device = fDevice.getAddr16(x, y); const uint8_t* alpha = mask.getAddr(x, y); SkPMColor16 srcColor = fPMColor16; unsigned devRB = fDevice.rowBytes() - (width << 1); unsigned maskRB = mask.fRowBytes - width; - + do { int w = width; do { @@ -384,15 +384,17 @@ class SkARGB4444_Shader_Blitter : public SkShaderBlitter { SkPMColor* fBuffer; uint8_t* fAAExpand; public: + SkARGB4444_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) : INHERITED(device, paint) { const int width = device.width(); fBuffer = (SkPMColor*)sk_malloc_throw(width * sizeof(SkPMColor) + width); fAAExpand = (uint8_t*)(fBuffer + width); - - (fXfermode = paint.getXfermode())->safeRef(); - + + fXfermode = paint.getXfermode(); + SkSafeRef(fXfermode); + unsigned flags = 0; if (!(fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) { flags |= SkBlitRow::kSrcPixelAlpha_Flag; @@ -405,41 +407,39 @@ SkARGB4444_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) SkBitmap::kARGB_4444_Config); } -virtual ~SkARGB4444_Shader_Blitter() -{ - fXfermode->safeUnref(); +virtual ~SkARGB4444_Shader_Blitter() { + SkSafeUnref(fXfermode); sk_free(fBuffer); } -virtual void blitH(int x, int y, int width) -{ +virtual void blitH(int x, int y, int width) { SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); - - SkPMColor16* device = fDevice.getAddr16(x, y); + + SkPMColor16* device = fDevice.getAddr16(x, y); SkPMColor* span = fBuffer; - + fShader->shadeSpan(x, y, span, width); if (fXfermode) { fXfermode->xfer4444(device, span, width, NULL); - } - else { + } else { fOpaqueProc(device, span, width, 0xFF, x, y); } } -virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) -{ +virtual void blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { SkPMColor* SK_RESTRICT span = fBuffer; uint8_t* SK_RESTRICT aaExpand = fAAExpand; SkPMColor16* device = fDevice.getAddr16(x, y); SkShader* shader = fShader; SkXfermode* xfer = fXfermode; - + if (NULL != xfer) { for (;;) { int count = *runs; - if (count <= 0) + if (count <= 0) { break; + } int aa = *antialias; if (aa) { shader->shadeSpan(x, y, span, count); @@ -458,12 +458,13 @@ virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t ru runs += count; antialias += count; x += count; - } + } } else { // no xfermode for (;;) { int count = *runs; - if (count <= 0) + if (count <= 0) { break; + } int aa = *antialias; if (aa) { fShader->shadeSpan(x, y, span, count); @@ -477,7 +478,7 @@ virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t ru runs += count; antialias += count; x += count; - } + } } } diff --git a/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp index 37ecdfc..f74fbe3 100644 --- a/src/core/SkBlitter_A8.cpp +++ b/src/core/SkBlitter_A8.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -21,71 +21,64 @@ #include "SkXfermode.h" SkA8_Blitter::SkA8_Blitter(const SkBitmap& device, const SkPaint& paint) - : INHERITED(device) -{ + : INHERITED(device) { fSrcA = SkColorGetA(paint.getColor()); } -const SkBitmap* SkA8_Blitter::justAnOpaqueColor(uint32_t* value) -{ - if (255 == fSrcA) - { +const SkBitmap* SkA8_Blitter::justAnOpaqueColor(uint32_t* value) { + if (255 == fSrcA) { *value = 255; return &fDevice; } return NULL; } -void SkA8_Blitter::blitH(int x, int y, int width) -{ - SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width()); +void SkA8_Blitter::blitH(int x, int y, int width) { + SkASSERT(x >= 0 && y >= 0 && + (unsigned)(x + width) <= (unsigned)fDevice.width()); - if (fSrcA == 0) + if (fSrcA == 0) { return; + } uint8_t* device = fDevice.getAddr8(x, y); - if (fSrcA == 255) - { + if (fSrcA == 255) { memset(device, 0xFF, width); - } - else - { + } else { unsigned scale = 256 - SkAlpha255To256(fSrcA); unsigned srcA = fSrcA; - for (int i = 0; i < width; i++) - { + for (int i = 0; i < width; i++) { device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale)); } } } -void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) -{ - if (fSrcA == 0) +void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { + if (fSrcA == 0) { return; + } uint8_t* device = fDevice.getAddr8(x, y); unsigned srcA = fSrcA; - for (;;) - { + for (;;) { int count = runs[0]; SkASSERT(count >= 0); - if (count == 0) + if (count == 0) { return; + } unsigned aa = antialias[0]; - if (aa == 255 && srcA == 255) + if (aa == 255 && srcA == 255) { memset(device, 0xFF, count); - else - { + } else { unsigned sa = SkAlphaMul(srcA, SkAlpha255To256(aa)); unsigned scale = 256 - sa; - for (int i = 0; i < count; i++) - { + for (int i = 0; i < count; i++) { device[i] = SkToU8(sa + SkAlphaMul(device[i], scale)); } } @@ -110,14 +103,14 @@ void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int1 } while (0) #define SK_BLITBWMASK_NAME SkA8_BlitBW -#define SK_BLITBWMASK_ARGS +#define SK_BLITBWMASK_ARGS #define SK_BLITBWMASK_BLIT8(mask, dst) solid_8_pixels(mask, dst) #define SK_BLITBWMASK_GETADDR getAddr8 #define SK_BLITBWMASK_DEVTYPE uint8_t #include "SkBlitBWMaskTemplate.h" -static inline void blend_8_pixels(U8CPU bw, uint8_t dst[], U8CPU sa, unsigned dst_scale) -{ +static inline void blend_8_pixels(U8CPU bw, uint8_t dst[], U8CPU sa, + unsigned dst_scale) { if (bw & 0x80) dst[0] = SkToU8(sa + SkAlphaMul(dst[0], dst_scale)); if (bw & 0x40) dst[1] = SkToU8(sa + SkAlphaMul(dst[1], dst_scale)); if (bw & 0x20) dst[2] = SkToU8(sa + SkAlphaMul(dst[2], dst_scale)); @@ -135,17 +128,18 @@ static inline void blend_8_pixels(U8CPU bw, uint8_t dst[], U8CPU sa, unsigned ds #define SK_BLITBWMASK_DEVTYPE uint8_t #include "SkBlitBWMaskTemplate.h" -void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) -{ - if (fSrcA == 0) +void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { + if (fSrcA == 0) { return; + } - if (mask.fFormat == SkMask::kBW_Format) - { - if (fSrcA == 0xFF) + if (mask.fFormat == SkMask::kBW_Format) { + if (fSrcA == 0xFF) { SkA8_BlitBW(fDevice, mask, clip); - else - SkA8_BlendBW(fDevice, mask, clip, fSrcA, SkAlpha255To256(255 - fSrcA)); + } else { + SkA8_BlendBW(fDevice, mask, clip, fSrcA, + SkAlpha255To256(255 - fSrcA)); + } return; } @@ -157,28 +151,24 @@ void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) const uint8_t* alpha = mask.getAddr(x, y); unsigned srcA = fSrcA; - while (--height >= 0) - { - for (int i = width - 1; i >= 0; --i) - { + while (--height >= 0) { + for (int i = width - 1; i >= 0; --i) { unsigned sa; // scale our src by the alpha value { int aa = alpha[i]; - if (aa == 0) + if (aa == 0) { continue; - - if (aa == 255) - { - if (srcA == 255) - { + } + if (aa == 255) { + if (srcA == 255) { device[i] = 0xFF; continue; } sa = srcA; - } - else + } else { sa = SkAlphaMul(srcA, SkAlpha255To256(aa)); + } } int scale = 256 - SkAlpha255To256(sa); @@ -189,63 +179,54 @@ void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) } } -/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -void SkA8_Blitter::blitV(int x, int y, int height, SkAlpha alpha) -{ - if (fSrcA == 0) +void SkA8_Blitter::blitV(int x, int y, int height, SkAlpha alpha) { + if (fSrcA == 0) { return; + } unsigned sa = SkAlphaMul(fSrcA, SkAlpha255To256(alpha)); uint8_t* device = fDevice.getAddr8(x, y); int rowBytes = fDevice.rowBytes(); - if (sa == 0xFF) - { - for (int i = 0; i < height; i++) - { + if (sa == 0xFF) { + for (int i = 0; i < height; i++) { *device = SkToU8(sa); device += rowBytes; } - } - else - { + } else { unsigned scale = 256 - SkAlpha255To256(sa); - for (int i = 0; i < height; i++) - { + for (int i = 0; i < height; i++) { *device = SkToU8(sa + SkAlphaMul(*device, scale)); device += rowBytes; } } } -void SkA8_Blitter::blitRect(int x, int y, int width, int height) -{ - SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width() && (unsigned)(y + height) <= (unsigned)fDevice.height()); +void SkA8_Blitter::blitRect(int x, int y, int width, int height) { + SkASSERT(x >= 0 && y >= 0 && + (unsigned)(x + width) <= (unsigned)fDevice.width() && + (unsigned)(y + height) <= (unsigned)fDevice.height()); - if (fSrcA == 0) + if (fSrcA == 0) { return; + } uint8_t* device = fDevice.getAddr8(x, y); unsigned srcA = fSrcA; - if (srcA == 255) - { - while (--height >= 0) - { + if (srcA == 255) { + while (--height >= 0) { memset(device, 0xFF, width); device += fDevice.rowBytes(); } - } - else - { + } else { unsigned scale = 256 - SkAlpha255To256(srcA); - while (--height >= 0) - { - for (int i = 0; i < width; i++) - { + while (--height >= 0) { + for (int i = 0; i < width; i++) { device[i] = SkToU8(srcA + SkAlphaMul(device[i], scale)); } device += fDevice.rowBytes(); @@ -256,10 +237,8 @@ void SkA8_Blitter::blitRect(int x, int y, int width, int height) /////////////////////////////////////////////////////////////////////// SkA8_Shader_Blitter::SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& paint) - : INHERITED(device, paint) -{ - if ((fXfermode = paint.getXfermode()) != NULL) - { + : INHERITED(device, paint) { + if ((fXfermode = paint.getXfermode()) != NULL) { fXfermode->ref(); SkASSERT(fShader); } @@ -269,33 +248,27 @@ SkA8_Shader_Blitter::SkA8_Shader_Blitter(const SkBitmap& device, const SkPaint& fAAExpand = (uint8_t*)(fBuffer + width); } -SkA8_Shader_Blitter::~SkA8_Shader_Blitter() -{ - if (fXfermode) fXfermode->safeUnref(); +SkA8_Shader_Blitter::~SkA8_Shader_Blitter() { + if (fXfermode) SkSafeUnref(fXfermode); sk_free(fBuffer); } -void SkA8_Shader_Blitter::blitH(int x, int y, int width) -{ - SkASSERT(x >= 0 && y >= 0 && (unsigned)(x + width) <= (unsigned)fDevice.width()); +void SkA8_Shader_Blitter::blitH(int x, int y, int width) { + SkASSERT(x >= 0 && y >= 0 && + (unsigned)(x + width) <= (unsigned)fDevice.width()); uint8_t* device = fDevice.getAddr8(x, y); - if ((fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) && fXfermode == NULL) - { + if ((fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) && !fXfermode) { memset(device, 0xFF, width); - } - else - { + } else { SkPMColor* span = fBuffer; fShader->shadeSpan(x, y, span, width); - if (fXfermode) + if (fXfermode) { fXfermode->xferA8(device, span, width, NULL); - else - { - for (int i = width - 1; i >= 0; --i) - { + } else { + for (int i = width - 1; i >= 0; --i) { unsigned srcA = SkGetPackedA32(span[i]); unsigned scale = 256 - SkAlpha255To256(srcA); @@ -305,8 +278,7 @@ void SkA8_Shader_Blitter::blitH(int x, int y, int width) } } -static inline uint8_t aa_blend8(SkPMColor src, U8CPU da, int aa) -{ +static inline uint8_t aa_blend8(SkPMColor src, U8CPU da, int aa) { SkASSERT((unsigned)aa <= 255); int src_scale = SkAlpha255To256(aa); @@ -316,8 +288,8 @@ static inline uint8_t aa_blend8(SkPMColor src, U8CPU da, int aa) return SkToU8((sa * src_scale + da * dst_scale) >> 8); } -void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) -{ +void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], + const int16_t runs[]) { SkShader* shader = fShader; SkXfermode* mode = fXfermode; uint8_t* aaExpand = fAAExpand; @@ -325,28 +297,24 @@ void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], con uint8_t* device = fDevice.getAddr8(x, y); int opaque = fShader->getFlags() & SkShader::kOpaqueAlpha_Flag; - for (;;) - { + for (;;) { int count = *runs; - if (count == 0) + if (count == 0) { break; + } int aa = *antialias; - if (aa) - { - if (opaque && aa == 255 && mode == NULL) + if (aa) { + if (opaque && aa == 255 && mode == NULL) { memset(device, 0xFF, count); - else - { + } else { shader->shadeSpan(x, y, span, count); - if (mode) - { + if (mode) { memset(aaExpand, aa, count); mode->xferA8(device, span, count, aaExpand); - } - else - { - for (int i = count - 1; i >= 0; --i) + } else { + for (int i = count - 1; i >= 0; --i) { device[i] = aa_blend8(span[i], device[i], aa); + } } } } @@ -354,17 +322,15 @@ void SkA8_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], con runs += count; antialias += count; x += count; - } + } } -void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) -{ - if (mask.fFormat == SkMask::kBW_Format) - { +void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { + if (mask.fFormat == SkMask::kBW_Format) { this->INHERITED::blitMask(mask, clip); return; } - + int x = clip.fLeft; int y = clip.fTop; int width = clip.width(); @@ -374,13 +340,12 @@ void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) SkPMColor* span = fBuffer; - while (--height >= 0) - { + while (--height >= 0) { fShader->shadeSpan(x, y, span, width); if (fXfermode) { fXfermode->xferA8(device, span, width, alpha); } - + y += 1; device += fDevice.rowBytes(); alpha += mask.fRowBytes; diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp index 37bd0da..a775adb 100644 --- a/src/core/SkBlitter_ARGB32.cpp +++ b/src/core/SkBlitter_ARGB32.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -40,6 +40,32 @@ using namespace skia_blitter_support; ////////////////////////////////////////////////////////////////////////////////////// +static void SkARGB32_Blit32(const SkBitmap& device, const SkMask& mask, + const SkIRect& clip, SkPMColor srcColor) { + U8CPU alpha = SkGetPackedA32(srcColor); + unsigned flags = SkBlitRow::kSrcPixelAlpha_Flag32; + if (alpha != 255) { + flags |= SkBlitRow::kGlobalAlpha_Flag32; + } + SkBlitRow::Proc32 proc = SkBlitRow::Factory32(flags); + + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + + SkPMColor* dstRow = device.getAddr32(x, y); + const SkPMColor* srcRow = reinterpret_cast<const SkPMColor*>(mask.getAddr(x, y)); + + do { + proc(dstRow, srcRow, width, alpha); + dstRow = (SkPMColor*)((char*)dstRow + device.rowBytes()); + srcRow = (const SkPMColor*)((const char*)srcRow + mask.fRowBytes); + } while (--height != 0); +} + +////////////////////////////////////////////////////////////////////////////////////// + SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint) : INHERITED(device) { uint32_t color = paint.getColor(); @@ -51,6 +77,7 @@ SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint) fSrcB = SkAlphaMul(SkColorGetB(color), scale); fPMColor = SkPackARGB32(fSrcA, fSrcR, fSrcG, fSrcB); + fColor32Proc = SkBlitRow::ColorProcFactory(); } const SkBitmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) { @@ -69,7 +96,8 @@ const SkBitmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) { void SkARGB32_Blitter::blitH(int x, int y, int width) { SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); - SkBlitRow::Color32(fDevice.getAddr32(x, y), width, fPMColor); + uint32_t* device = fDevice.getAddr32(x, y); + fColor32Proc(device, device, width, fPMColor); } void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], @@ -94,7 +122,7 @@ void SkARGB32_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], sk_memset32(device, color, count); } else { uint32_t sc = SkAlphaMulQ(color, SkAlpha255To256(aa)); - SkBlitRow::Color32(device, count, sc); + fColor32Proc(device, device, count, sc); } } runs += count; @@ -154,6 +182,9 @@ void SkARGB32_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { if (mask.fFormat == SkMask::kBW_Format) { SkARGB32_BlendBW(fDevice, mask, clip, fPMColor, SkAlpha255To256(255 - fSrcA)); return; + } else if (SkMask::kARGB32_Format == mask.fFormat) { + SkARGB32_Blit32(fDevice, mask, clip, fPMColor); + return; } int x = clip.fLeft; @@ -186,7 +217,10 @@ void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask, if (mask.fFormat == SkMask::kBW_Format) { SkARGB32_BlitBW(fDevice, mask, clip, fPMColor); return; - } + } else if (SkMask::kARGB32_Format == mask.fFormat) { + SkARGB32_Blit32(fDevice, mask, clip, fPMColor); + return; + } int x = clip.fLeft; int y = clip.fTop; @@ -286,7 +320,7 @@ void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) { size_t rowBytes = fDevice.rowBytes(); while (--height >= 0) { - SkBlitRow::Color32(device, width, color); + fColor32Proc(device, device, width, color); device = (uint32_t*)((char*)device + rowBytes); } } @@ -304,6 +338,8 @@ void SkARGB32_Black_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { SkPMColor black = (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT); SkARGB32_BlitBW(fDevice, mask, clip, black); + } else if (SkMask::kARGB32_Format == mask.fFormat) { + SkARGB32_Blit32(fDevice, mask, clip, fPMColor); } else { #if defined(SK_SUPPORT_LCDTEXT) const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format; @@ -412,7 +448,7 @@ SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device, } SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() { - fXfermode->safeUnref(); + SkSafeUnref(fXfermode); sk_free(fBuffer); } @@ -465,7 +501,7 @@ void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], runs += count; antialias += count; x += count; - } + } } else if (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) { for (;;) { int count = *runs; @@ -486,7 +522,7 @@ void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], runs += count; antialias += count; x += count; - } + } } else { // no xfermode but the shader not opaque for (;;) { int count = *runs; @@ -506,6 +542,6 @@ void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], runs += count; antialias += count; x += count; - } + } } } diff --git a/src/core/SkBlitter_ARGB32_Subpixel.cpp b/src/core/SkBlitter_ARGB32_Subpixel.cpp new file mode 100644 index 0000000..5d334ae --- /dev/null +++ b/src/core/SkBlitter_ARGB32_Subpixel.cpp @@ -0,0 +1,144 @@ +/* libs/graphics/sgl/SkBlitter_ARGB32_Subpixel.cpp +** +** Copyright 2009, 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. +*/ + +/* LCD blend functions: + + These functions take an alpha pixel of the following form: + red, green, blue -> an alpha value for the given colour component. + alpha -> the max of the red, green and blue alpha values. + + These alpha pixels result from subpixel renderering. The R/G/B values have + already been corrected for RGB/BGR element ordering. + + The alpha pixel is blended with an original pixel and a source colour, + resulting in a new pixel value. +*/ + +#include "SkBitmap.h" +#include "SkColorPriv.h" +#include "SkMask.h" +#include "SkRect.h" + +namespace skia_blitter_support { + +/** Given a clip region which describes the desired location of a glyph and a + bitmap to which an LCD glyph is to be blitted, return a pointer to the + SkBitmap's pixels and output width and height adjusts for the glyph as well + as a pointer into the glyph. + + Recall that LCD glyphs have extra rows (vertical mode) or columns + (horizontal mode) at the edges as a result of low-pass filtering. If we + wanted to put a glyph on the hard-left edge of bitmap, we would have to know + to start one pixel into the glyph, as well as to only add 1 to the recorded + glyph width etc. This function encapsulates that behaviour. + + @param mask The glyph to be blitted. + @param clip The clip region describing the desired location of the glyph. + @param device The SkBitmap target for the blit. + @param widthAdjustment (output) a number to add to the glyph's nominal width. + @param heightAdjustment (output) a number to add to the glyph's nominal width. + @param alpha32 (output) a pointer into the 32-bit subpixel alpha data for the glyph +*/ +uint32_t* adjustForSubpixelClip(const SkMask& mask, + const SkIRect& clip, const SkBitmap& device, + int* widthAdjustment, int* heightAdjustment, + const uint32_t** alpha32) { + const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format; + const bool verticalLCDMode = mask.fFormat == SkMask::kVerticalLCD_Format; + const int leftOffset = clip.fLeft > 0 ? lcdMode : 0; + const int topOffset = clip.fTop > 0 ? verticalLCDMode : 0; + const int rightOffset = lcdMode && clip.fRight < device.width(); + const int bottomOffset = verticalLCDMode && clip.fBottom < device.height(); + + uint32_t* device32 = device.getAddr32(clip.fLeft - leftOffset, clip.fTop - topOffset); + *alpha32 = mask.getAddrLCD(clip.fLeft + (lcdMode && !leftOffset), + clip.fTop + (verticalLCDMode && !topOffset)); + + *widthAdjustment = leftOffset + rightOffset; + *heightAdjustment = topOffset + bottomOffset; + + return device32; +} + +uint32_t BlendLCDPixelWithColor(const uint32_t alphaPixel, const uint32_t originalPixel, + const uint32_t sourcePixel) { + unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel)); + unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel)); + unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel)); + + unsigned sourceRed = SkGetPackedR32(sourcePixel); + unsigned sourceGreen = SkGetPackedG32(sourcePixel); + unsigned sourceBlue = SkGetPackedB32(sourcePixel); + unsigned sourceAlpha = SkAlpha255To256(SkGetPackedA32(sourcePixel)); + + alphaRed = (alphaRed * sourceAlpha) >> 8; + alphaGreen = (alphaGreen * sourceAlpha) >> 8; + alphaBlue = (alphaBlue * sourceAlpha) >> 8; + unsigned alphaAlpha = SkMax32(SkMax32(alphaRed, alphaBlue), alphaGreen); + + unsigned originalRed = SkGetPackedR32(originalPixel); + unsigned originalGreen = SkGetPackedG32(originalPixel); + unsigned originalBlue = SkGetPackedB32(originalPixel); + unsigned originalAlpha = SkGetPackedA32(originalPixel); + + return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha), + ((sourceRed * alphaRed) >> 8) + ((originalRed * (256 - alphaRed)) >> 8), + ((sourceGreen * alphaGreen) >> 8) + ((originalGreen * (256 - alphaGreen)) >> 8), + ((sourceBlue * alphaBlue) >> 8) + ((originalBlue * (256 - alphaBlue)) >> 8)); + +} + +uint32_t BlendLCDPixelWithOpaqueColor(const uint32_t alphaPixel, const uint32_t originalPixel, + const uint32_t sourcePixel) { + unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel)); + unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel)); + unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel)); + unsigned alphaAlpha = SkGetPackedA32(alphaPixel); + + unsigned sourceRed = SkGetPackedR32(sourcePixel); + unsigned sourceGreen = SkGetPackedG32(sourcePixel); + unsigned sourceBlue = SkGetPackedB32(sourcePixel); + + unsigned originalRed = SkGetPackedR32(originalPixel); + unsigned originalGreen = SkGetPackedG32(originalPixel); + unsigned originalBlue = SkGetPackedB32(originalPixel); + unsigned originalAlpha = SkGetPackedA32(originalPixel); + + return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha), + ((sourceRed * alphaRed) >> 8) + ((originalRed * (256 - alphaRed)) >> 8), + ((sourceGreen * alphaGreen) >> 8) + ((originalGreen * (256 - alphaGreen)) >> 8), + ((sourceBlue * alphaBlue) >> 8) + ((originalBlue * (256 - alphaBlue)) >> 8)); +} + +uint32_t BlendLCDPixelWithBlack(const uint32_t alphaPixel, const uint32_t originalPixel) { + unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel)); + unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel)); + unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel)); + unsigned alphaAlpha = SkGetPackedA32(alphaPixel); + + unsigned originalRed = SkGetPackedR32(originalPixel); + unsigned originalGreen = SkGetPackedG32(originalPixel); + unsigned originalBlue = SkGetPackedB32(originalPixel); + unsigned originalAlpha = SkGetPackedA32(originalPixel); + + return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha), + (originalRed * (256 - alphaRed)) >> 8, + (originalGreen * (256 - alphaGreen)) >> 8, + (originalBlue * (256 - alphaBlue)) >> 8); +} + +} // namespace skia_blitter_support diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 94c2439..6126850 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -74,6 +74,9 @@ struct DeviceCM { const SkMatrix* fMatrix; SkPaint* fPaint; // may be null (in the future) int16_t fX, fY; // relative to base matrix/clip + // optional, related to canvas' external matrix + const SkMatrix* fMVMatrix; + const SkMatrix* fExtMatrix; DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint) : fNext(NULL) { @@ -81,7 +84,7 @@ struct DeviceCM { device->ref(); device->lockPixels(); } - fDevice = device; + fDevice = device; fX = SkToS16(x); fY = SkToS16(y); fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL; @@ -94,14 +97,14 @@ struct DeviceCM { } SkDELETE(fPaint); } - + void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip, SkRegion* updateClip) { int x = fX; int y = fY; int width = fDevice->width(); int height = fDevice->height(); - + if ((x | y) == 0) { fMatrix = &totalMatrix; fClip = totalClip; @@ -110,19 +113,19 @@ struct DeviceCM { fMatrixStorage.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); fMatrix = &fMatrixStorage; - + totalClip.translate(-x, -y, &fClip); } fClip.op(0, 0, width, height, SkRegion::kIntersect_Op); // intersect clip, but don't translate it (yet) - + if (updateClip) { updateClip->op(x, y, x + width, y + height, SkRegion::kDifference_Op); } - + fDevice->setMatrixClip(*fMatrix, fClip); #ifdef SK_DEBUG @@ -132,8 +135,18 @@ struct DeviceCM { SkASSERT(deviceR.contains(fClip.getBounds())); } #endif + // default is to assume no external matrix + fMVMatrix = NULL; + fExtMatrix = NULL; + } + + // can only be called after calling updateMC() + void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) { + fMVMatrixStorage.setConcat(extI, *fMatrix); + fMVMatrix = &fMVMatrixStorage; + fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas) } - + void translateClip() { if (fX | fY) { fClip.translate(fX, fY); @@ -141,7 +154,7 @@ struct DeviceCM { } private: - SkMatrix fMatrixStorage; + SkMatrix fMatrixStorage, fMVMatrixStorage; }; /* This is the record we keep for each save/restore level in the stack. @@ -157,7 +170,7 @@ public: SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec SkRegion* fRegion; // points to either fRegionStorage or prev MCRec SkDrawFilter* fFilter; // the current filter (or null) - + DeviceCM* fLayer; /* If there are any layers in the stack, this points to the top-most one that is at or below this level in the stack (so we know what @@ -175,7 +188,7 @@ public: } else { fMatrix = prev->fMatrix; } - + if (flags & SkCanvas::kClip_SaveFlag) { fRegionStorage = *prev->fRegion; fRegion = &fRegionStorage; @@ -184,12 +197,12 @@ public: } fFilter = prev->fFilter; - fFilter->safeRef(); + SkSafeRef(fFilter); fTopLayer = prev->fTopLayer; } else { // no prev fMatrixStorage.reset(); - + fMatrix = &fMatrixStorage; fRegion = &fRegionStorage; fFilter = NULL; @@ -201,11 +214,11 @@ public: inc_rec(); } ~MCRec() { - fFilter->safeUnref(); + SkSafeUnref(fFilter); SkDELETE(fLayer); dec_rec(); } - + private: SkMatrix fMatrixStorage; SkRegion fRegionStorage; @@ -221,7 +234,7 @@ public: fCurrLayer = canvas->fMCRec->fTopLayer; fSkipEmptyClips = skipEmptyClips; } - + bool next() { // skip over recs with empty clips if (fSkipEmptyClips) { @@ -240,21 +253,22 @@ public: fLayerX = rec->fX; fLayerY = rec->fY; fPaint = rec->fPaint; + fMVMatrix = rec->fMVMatrix; + fExtMatrix = rec->fExtMatrix; SkDEBUGCODE(this->validate();) fCurrLayer = rec->fNext; if (fBounder) { fBounder->setClip(fClip); } - // fCurrLayer may be NULL now - fCanvas->prepareForDeviceDraw(fDevice); + fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip); return true; } return false; } - + int getX() const { return fLayerX; } int getY() const { return fLayerY; } SkDevice* getDevice() const { return fDevice; } @@ -296,7 +310,7 @@ public: fLooper->restore(); } } - + bool next() { SkDrawFilter* filter = fFilter; @@ -306,9 +320,9 @@ public: filter->restore(fCanvas, fPaint, fType); fNeedFilterRestore = false; } - + bool result; - + if (NULL != fLooper) { result = fLooper->next(); } else { @@ -323,7 +337,7 @@ public: } return result; } - + private: SkDrawLooper* fLooper; SkDrawFilter* fFilter; @@ -332,7 +346,7 @@ private: SkDrawFilter::Type fType; bool fOnce; bool fNeedFilterRestore; - + }; /* Stack helper for managing a SkBounder. In the destructor, if we were @@ -382,7 +396,7 @@ private: while (looper.next()) { \ SkAutoBounderCommit ac(fBounder); \ SkDrawIter iter(this); - + #define ITER_END } //////////////////////////////////////////////////////////////////////////// @@ -401,6 +415,8 @@ SkDevice* SkCanvas::init(SkDevice* device) { fMCRec->fTopLayer = fMCRec->fLayer; fMCRec->fNext = NULL; + fUseExternalMatrix = false; + return this->setDevice(device); } @@ -427,7 +443,7 @@ SkCanvas::SkCanvas(const SkBitmap& bitmap) : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) { inc_canvas(); - SkDevice* device = SkNEW_ARGS(SkDevice, (bitmap)); + SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false)); fDeviceFactory = device->getDeviceFactory(); this->init(device)->unref(); } @@ -462,7 +478,7 @@ SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) { SkDevice* SkCanvas::getDevice() const { // return root device SkDeque::Iter iter(fMCStack); - MCRec* rec = (MCRec*)iter.next(); + MCRec* rec = (MCRec*)iter.next(); SkASSERT(rec && rec->fLayer); return rec->fLayer->fDevice; } @@ -470,14 +486,14 @@ SkDevice* SkCanvas::getDevice() const { SkDevice* SkCanvas::setDevice(SkDevice* device) { // return root device SkDeque::Iter iter(fMCStack); - MCRec* rec = (MCRec*)iter.next(); + MCRec* rec = (MCRec*)iter.next(); SkASSERT(rec && rec->fLayer); SkDevice* rootDevice = rec->fLayer->fDevice; if (rootDevice == device) { return device; } - + /* Notify the devices that they are going in/out of scope, so they can do things like lock/unlock their pixels, etc. */ @@ -492,15 +508,15 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) { rootDevice = device; fDeviceCMDirty = true; - + /* Now we update our initial region to have the bounds of the new device, and then intersect all of the clips in our stack with these bounds, to ensure that we can't draw outside of the device's bounds (and trash memory). - + NOTE: this is only a partial-fix, since if the new device is larger than the previous one, we don't know how to "enlarge" the clips in our stack, - so drawing may be artificially restricted. Without keeping a history of + so drawing may be artificially restricted. Without keeping a history of all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly reconstruct the correct clips, so this approximation will have to do. The caller really needs to restore() back to the base if they want to @@ -515,7 +531,7 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) { } else { // compute our total bounds for all devices SkIRect bounds; - + bounds.set(0, 0, device->width(), device->height()); // now jam our 1st clip to be bounds, and intersect the rest with that @@ -527,12 +543,45 @@ SkDevice* SkCanvas::setDevice(SkDevice* device) { return device; } -SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) { - SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap))); +SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap, bool forLayer) { + SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (this, bitmap, forLayer))); device->unref(); return device; } +bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) { + SkDevice* device = this->getDevice(); + if (!device) { + return false; + } + return device->readPixels(srcRect, bitmap); +} + +SkDeviceFactory* SkCanvas::setDeviceFactory(SkDeviceFactory* factory) { + SkDELETE(fDeviceFactory); + fDeviceFactory = factory; + return factory; +} + +////////////////////////////////////////////////////////////////////////////// + +bool SkCanvas::readPixels(SkBitmap* bitmap) { + SkDevice* device = this->getDevice(); + if (!device) { + return false; + } + SkIRect bounds; + bounds.set(0, 0, device->width(), device->height()); + return this->readPixels(bounds, bitmap); +} + +void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) { + SkDevice* device = this->getDevice(); + if (device) { + device->writePixels(bitmap, x, y); + } +} + ////////////////////////////////////////////////////////////////////////////// bool SkCanvas::getViewport(SkIPoint* size) const { @@ -546,8 +595,9 @@ bool SkCanvas::getViewport(SkIPoint* size) const { bool SkCanvas::setViewport(int width, int height) { if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0) return false; - this->setDevice(createDevice(SkBitmap::kARGB_8888_Config, width, height, - false, false))->unref(); + + this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height, + false, false))->unref(); return true; } @@ -556,24 +606,33 @@ void SkCanvas::updateDeviceCMCache() { const SkMatrix& totalMatrix = this->getTotalMatrix(); const SkRegion& totalClip = this->getTotalClip(); DeviceCM* layer = fMCRec->fTopLayer; - + if (NULL == layer->fNext) { // only one layer layer->updateMC(totalMatrix, totalClip, NULL); + if (fUseExternalMatrix) { + layer->updateExternalMatrix(fExternalMatrix, + fExternalInverse); + } } else { SkRegion clip; clip = totalClip; // make a copy do { layer->updateMC(totalMatrix, clip, &clip); + if (fUseExternalMatrix) { + layer->updateExternalMatrix(fExternalMatrix, + fExternalInverse); + } } while ((layer = layer->fNext) != NULL); } fDeviceCMDirty = false; } } -void SkCanvas::prepareForDeviceDraw(SkDevice* device) { +void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix, + const SkRegion& clip) { SkASSERT(device); if (fLastDeviceToGainFocus != device) { - device->gainFocus(this); + device->gainFocus(this, matrix, clip); fLastDeviceToGainFocus = device; } } @@ -582,13 +641,13 @@ void SkCanvas::prepareForDeviceDraw(SkDevice* device) { int SkCanvas::internalSave(SaveFlags flags) { int saveCount = this->getSaveCount(); // record this before the actual save - + MCRec* newTop = (MCRec*)fMCStack.push_back(); new (newTop) MCRec(fMCRec, flags); // balanced in restore() - + newTop->fNext = fMCRec; fMCRec = newTop; - + return saveCount; } @@ -651,10 +710,13 @@ int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SkIRect ir; const SkIRect& clipBounds = this->getTotalClip().getBounds(); + if (clipBounds.isEmpty()) { + return count; + } if (NULL != bounds) { SkRect r; - + this->getTotalMatrix().mapRect(&r, *bounds); r.roundOut(&ir); // early exit if the layer's bounds are clipped out @@ -756,11 +818,14 @@ void SkCanvas::restoreToCount(int count) { // can't draw it if its empty, or its too big for a fixed-point width or height static bool reject_bitmap(const SkBitmap& bitmap) { - return bitmap.width() <= 0 || bitmap.height() <= 0 || - bitmap.width() > 32767 || bitmap.height() > 32767; + return bitmap.width() <= 0 || bitmap.height() <= 0 +#ifndef SK_ALLOW_OVER_32K_BITMAPS + || bitmap.width() > 32767 || bitmap.height() > 32767 +#endif + ; } -void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, +void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect, const SkMatrix& matrix, const SkPaint* paint) { if (reject_bitmap(bitmap)) { return; @@ -768,9 +833,9 @@ void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, if (NULL == paint) { SkPaint tmpPaint; - this->commonDrawBitmap(bitmap, matrix, tmpPaint); + this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint); } else { - this->commonDrawBitmap(bitmap, matrix, *paint); + this->commonDrawBitmap(bitmap, srcRect, matrix, *paint); } } @@ -781,7 +846,7 @@ void SkCanvas::drawDevice(SkDevice* device, int x, int y, tmp.setDither(true); paint = &tmp; } - + ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type) while (iter.next()) { iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(), @@ -838,7 +903,7 @@ void SkCanvas::setMatrix(const SkMatrix& matrix) { // will see its action void SkCanvas::resetMatrix() { SkMatrix matrix; - + matrix.reset(); this->setMatrix(matrix); } @@ -887,7 +952,7 @@ bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) { SkRegion base; const SkBitmap& bm = this->getDevice()->accessBitmap(false); base.setRect(0, 0, bm.width(), bm.height()); - + if (SkRegion::kReplace_Op == op) { return fMCRec->fRegion->setPath(devPath, base); } else { @@ -926,6 +991,10 @@ void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const { antialiasing (worst case) */ bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const { + + if (!rect.hasValidCoordinates()) + return true; + if (fMCRec->fRegion->isEmpty()) { return true; } @@ -967,16 +1036,16 @@ bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const { if (fMCRec->fRegion->isEmpty()) { return true; } - + SkScalarCompareType userT = SkScalarToCompareType(top); SkScalarCompareType userB = SkScalarToCompareType(bottom); - + // check for invalid user Y coordinates (i.e. empty) // reed: why do we need to do this check, since it slows us down? if (userT >= userB) { return true; } - + // check if we are above or below the local clip bounds const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(); return userT >= clipR.fBottom || userB <= clipR.fTop; @@ -1008,7 +1077,7 @@ bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const { int inset = (kAA_EdgeType == et); r.iset(ibounds.fLeft - inset, ibounds.fTop - inset, ibounds.fRight + inset, ibounds.fBottom + inset); - + // invert into local coordinates inverse.mapRect(bounds, r); } @@ -1023,13 +1092,24 @@ const SkRegion& SkCanvas::getTotalClip() const { return *fMCRec->fRegion; } -/////////////////////////////////////////////////////////////////////////////// +void SkCanvas::setExternalMatrix(const SkMatrix* matrix) { + if (NULL == matrix || matrix->isIdentity()) { + if (fUseExternalMatrix) { + fDeviceCMDirty = true; + } + fUseExternalMatrix = false; + } else { + fUseExternalMatrix = true; + fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix) -SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, - int height, bool isOpaque, bool isForLayer) { + fExternalMatrix = *matrix; + matrix->invert(&fExternalInverse); + } +} - return fDeviceFactory->newDevice(config, width, height, isOpaque, - isForLayer); +SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height, + bool isOpaque, bool forLayer) { + return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer); } ////////////////////////////////////////////////////////////////////////////// @@ -1055,11 +1135,11 @@ void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], SkASSERT(pts != NULL); ITER_BEGIN(paint, SkDrawFilter::kPoint_Type) - + while (iter.next()) { iter.fDevice->drawPoints(iter, mode, count, pts, paint); } - + ITER_END } @@ -1071,7 +1151,7 @@ void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) { return; } } - + ITER_BEGIN(paint, SkDrawFilter::kRect_Type) while (iter.next()) { @@ -1113,10 +1193,10 @@ void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, return; } } - + SkMatrix matrix; matrix.setTranslate(x, y); - this->internalDrawBitmap(bitmap, matrix, paint); + this->internalDrawBitmap(bitmap, NULL, matrix, paint); } void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src, @@ -1124,22 +1204,14 @@ void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src, if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) { return; } - + // do this now, to avoid the cost of calling extract for RLE bitmaps if (this->quickReject(dst, paint2EdgeType(paint))) { return; } - - SkBitmap tmp; // storage if we need a subset of bitmap + const SkBitmap* bitmapPtr = &bitmap; - if (NULL != src) { - if (!bitmap.extractSubset(&tmp, *src)) { - return; // extraction failed - } - bitmapPtr = &tmp; - } - SkMatrix matrix; SkRect tmpSrc; if (src) { @@ -1159,23 +1231,32 @@ void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src, SkIntToScalar(bitmap.height())); } matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); - this->internalDrawBitmap(*bitmapPtr, matrix, paint); + + // ensure that src is "valid" before we pass it to our internal routines + // and to SkDevice. i.e. sure it is contained inside the original bitmap. + SkIRect tmpISrc; + if (src) { + tmpISrc.set(0, 0, bitmap.width(), bitmap.height()); + tmpISrc.intersect(*src); + src = &tmpISrc; + } + this->internalDrawBitmap(*bitmapPtr, src, matrix, paint); } void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { SkDEBUGCODE(bitmap.validate();) - this->internalDrawBitmap(bitmap, matrix, paint); + this->internalDrawBitmap(bitmap, NULL, matrix, paint); } -void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, - const SkPaint& paint) { +void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect, + const SkMatrix& matrix, const SkPaint& paint) { SkDEBUGCODE(bitmap.validate();) ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type) while (iter.next()) { - iter.fDevice->drawBitmap(iter, bitmap, matrix, paint); + iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint); } ITER_END @@ -1184,18 +1265,18 @@ void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) { SkDEBUGCODE(bitmap.validate();) - + if (reject_bitmap(bitmap)) { return; } - + SkPaint tmp; if (NULL == paint) { paint = &tmp; } - + ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type) - + while (iter.next()) { iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(), *paint); @@ -1217,12 +1298,12 @@ void SkCanvas::drawText(const void* text, size_t byteLength, void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) { ITER_BEGIN(paint, SkDrawFilter::kText_Type) - + while (iter.next()) { iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2, paint); } - + ITER_END } @@ -1230,12 +1311,12 @@ void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint) { ITER_BEGIN(paint, SkDrawFilter::kText_Type) - + while (iter.next()) { iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1, paint); } - + ITER_END } @@ -1272,12 +1353,12 @@ void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const uint16_t indices[], int indexCount, const SkPaint& paint) { ITER_BEGIN(paint, SkDrawFilter::kPath_Type) - + while (iter.next()) { iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs, colors, xmode, indices, indexCount, paint); } - + ITER_END } @@ -1313,7 +1394,7 @@ void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) { void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) { SkPoint pt; - + pt.set(x, y); this->drawPoints(kPoints_PointMode, 1, &pt, paint); } @@ -1321,7 +1402,7 @@ void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) { void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) { SkPoint pt; SkPaint paint; - + pt.set(x, y); paint.setColor(color); this->drawPoints(kPoints_PointMode, 1, &pt, paint); @@ -1330,7 +1411,7 @@ void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) { void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) { SkPoint pts[2]; - + pts[0].set(x0, y0); pts[1].set(x1, y1); this->drawPoints(kLines_PointMode, 2, pts, paint); @@ -1353,7 +1434,7 @@ void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, SkRect r; r.set(cx - radius, cy - radius, cx + radius, cy + radius); - + if (paint.canComputeFastBounds()) { SkRect storage; if (this->quickReject(paint.computeFastBounds(r, &storage), @@ -1361,7 +1442,7 @@ void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, return; } } - + SkPath path; path.addOval(r); this->drawPath(path, paint); @@ -1422,7 +1503,7 @@ void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength, const SkPath& path, SkScalar hOffset, SkScalar vOffset, const SkPaint& paint) { SkMatrix matrix; - + matrix.setTranslate(hOffset, vOffset); this->drawTextOnPath(text, byteLength, path, &matrix, paint); } @@ -1444,8 +1525,7 @@ void SkCanvas::drawShape(SkShape* shape) { /////////////////////////////////////////////////////////////////////////////// SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) { - // need COMPILE_TIME_ASSERT - SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter)); + SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small); SkASSERT(canvas); diff --git a/src/core/SkChunkAlloc.cpp b/src/core/SkChunkAlloc.cpp index 83272c5..e0c03a8 100644 --- a/src/core/SkChunkAlloc.cpp +++ b/src/core/SkChunkAlloc.cpp @@ -49,6 +49,11 @@ struct SkChunkAlloc::Block { } return block; } + + bool contains(const void* addr) const { + const char* ptr = reinterpret_cast<const char*>(addr); + return ptr >= (const char*)(this + 1) && ptr < fFreePtr; + } }; SkChunkAlloc::SkChunkAlloc(size_t minSize) @@ -85,7 +90,9 @@ SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) { return block; } - size_t size = SkMax32((int32_t)bytes, (int32_t)fMinSize); + size_t size = bytes; + if (size < fMinSize) + size = fMinSize; block = (Block*)sk_malloc_flags(sizeof(Block) + size, ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0); @@ -137,3 +144,14 @@ size_t SkChunkAlloc::unalloc(void* ptr) { return bytes; } +bool SkChunkAlloc::contains(const void* addr) const { + const Block* block = fBlock; + while (block) { + if (block->contains(addr)) { + return true; + } + block = block->fNext; + } + return false; +} + diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp index 7e566a3..62bb2d6 100644 --- a/src/core/SkComposeShader.cpp +++ b/src/core/SkComposeShader.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -20,40 +20,36 @@ #include "SkColorPriv.h" #include "SkXfermode.h" -////////////////////////////////////////////////////////////////////////////////////// - -SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) -{ +/////////////////////////////////////////////////////////////////////////////// + +SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) { fShaderA = sA; sA->ref(); fShaderB = sB; sB->ref(); // mode may be null - fMode = mode; mode->safeRef(); + fMode = mode; + SkSafeRef(mode); } SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) : - INHERITED(buffer) -{ + INHERITED(buffer) { fShaderA = static_cast<SkShader*>(buffer.readFlattenable()); fShaderB = static_cast<SkShader*>(buffer.readFlattenable()); fMode = static_cast<SkXfermode*>(buffer.readFlattenable()); } -SkComposeShader::~SkComposeShader() -{ - fMode->safeUnref(); // may be null +SkComposeShader::~SkComposeShader() { + SkSafeUnref(fMode); fShaderB->unref(); fShaderA->unref(); } -void SkComposeShader::beginSession() -{ +void SkComposeShader::beginSession() { this->INHERITED::beginSession(); fShaderA->beginSession(); fShaderB->beginSession(); } -void SkComposeShader::endSession() -{ +void SkComposeShader::endSession() { fShaderA->endSession(); fShaderB->endSession(); this->INHERITED::endSession(); @@ -61,14 +57,13 @@ void SkComposeShader::endSession() class SkAutoAlphaRestore { public: - SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) - { + SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) { fAlpha = paint->getAlpha(); fPaint = paint; paint->setAlpha(newAlpha); } - ~SkAutoAlphaRestore() - { + + ~SkAutoAlphaRestore() { fPaint->setAlpha(fAlpha); } private: @@ -76,8 +71,7 @@ private: uint8_t fAlpha; }; -void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) -{ +void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) { this->INHERITED::flatten(buffer); buffer.writeFlattenable(fShaderA); buffer.writeFlattenable(fShaderB); @@ -90,19 +84,19 @@ void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) */ bool SkComposeShader::setContext(const SkBitmap& device, const SkPaint& paint, - const SkMatrix& matrix) -{ - if (!this->INHERITED::setContext(device, paint, matrix)) + const SkMatrix& matrix) { + if (!this->INHERITED::setContext(device, paint, matrix)) { return false; + } // we preconcat our localMatrix (if any) with the device matrix // before calling our sub-shaders SkMatrix tmpM; - + (void)this->getLocalMatrix(&tmpM); tmpM.setConcat(matrix, tmpM); - + SkAutoAlphaRestore restore(const_cast<SkPaint*>(&paint), 0xFF); return fShaderA->setContext(device, paint, tmpM) && @@ -113,58 +107,56 @@ bool SkComposeShader::setContext(const SkBitmap& device, // take up too much stack-space (each element is 4 bytes) #define TMP_COLOR_COUNT 64 -void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) -{ +void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) { SkShader* shaderA = fShaderA; SkShader* shaderB = fShaderB; SkXfermode* mode = fMode; unsigned scale = SkAlpha255To256(this->getPaintAlpha()); - + SkPMColor tmp[TMP_COLOR_COUNT]; - if (NULL == mode) // implied SRC_OVER - { + if (NULL == mode) { // implied SRC_OVER // TODO: when we have a good test-case, should use SkBlitRow::Proc32 // for these loops do { int n = count; - if (n > TMP_COLOR_COUNT) + if (n > TMP_COLOR_COUNT) { n = TMP_COLOR_COUNT; - + } + shaderA->shadeSpan(x, y, result, n); shaderB->shadeSpan(x, y, tmp, n); - if (256 == scale) - { - for (int i = 0; i < n; i++) + if (256 == scale) { + for (int i = 0; i < n; i++) { result[i] = SkPMSrcOver(tmp[i], result[i]); + } + } else { + for (int i = 0; i < n; i++) { + result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), + scale); + } } - else - { - for (int i = 0; i < n; i++) - result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), scale); - } - + result += n; x += n; count -= n; } while (count > 0); - } - else // use mode for the composition - { + } else { // use mode for the composition do { int n = count; - if (n > TMP_COLOR_COUNT) + if (n > TMP_COLOR_COUNT) { n = TMP_COLOR_COUNT; - + } + shaderA->shadeSpan(x, y, result, n); shaderB->shadeSpan(x, y, tmp, n); mode->xfer32(result, tmp, n, NULL); - if (256 == scale) - { - for (int i = 0; i < n; i++) + if (256 == scale) { + for (int i = 0; i < n; i++) { result[i] = SkAlphaMulQ(result[i], scale); + } } result += n; @@ -173,4 +165,4 @@ void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) } while (count > 0); } } - + diff --git a/src/core/SkConcaveToTriangles.cpp b/src/core/SkConcaveToTriangles.cpp new file mode 100644 index 0000000..fa718e5 --- /dev/null +++ b/src/core/SkConcaveToTriangles.cpp @@ -0,0 +1,967 @@ +/* + * Copyright (C) 2009 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 is an implementation of the triangulation algorithm described by Alain +// Fournier and Delfin Montuno, in "Triangulating Simple Polygons and Equivalent +// Problems", in the ACM Transactions on Graphics, vol. 3, no. 2, April 1984, +// pp. 153-174. +// +// No new vertices are created in the triangulation: triangles are constructed +// only from the points in the original polygon, so there is no possibility for +// cracks to develop from finite precision arithmetic. +//////////////////////////////////////////////////////////////////////////////// + +// TODO: +// - RemoveDegenerateTrapezoids() was added to make the code robust, but it +// unfortunately introduces T-vertices. Make it robust without T-vertices. +// - It should be easy enough to detect whether the outer contour is right- or +// left-handed by looking at the top vertex, which is available in the +// pre-sort during trapezoidization. Use this information in angleIsConvex() +// to allowed either handedness outer contour. In either case, though, holes +// need to have the opposite orientation. +// - SkTHeapSort was broken, so I wrote a bubble sort so that I could make other +// things work. Use SkQSort() instead. +// - The ActiveTrapezoid array does a linear search which is O(n) inefficient. +// Use SkSearch to implement O(log n) binary search and insertion sort. +// - There is no need to use SkTDArray for everything. Use SkAutoTMalloc for +// everything else. + +#include "SkTDArray.h" +#include "SkGeometry.h" +#include "SkTSort.h" + +// This is used to prevent runaway code bugs, and can probably be removed after +// the code has been proven robust. +#define kMaxCount 1000 + +#define DEBUG +#ifdef DEBUG +//------------------------------------------------------------------------------ +// Debugging support +//------------------------------------------------------------------------------ + +#include <cstdio> +#include <stdarg.h> + +static int gDebugLevel = 0; // This enables debug reporting. + +static void DebugPrintf(const char *format, ...) { + if (gDebugLevel > 0) { + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + } +} + +static void FailureMessage(const char *format, ...) { + if (1) { + printf("FAILURE: "); + va_list ap; + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + } +} +#else // !DEBUG +inline void DebugPrintf(const char *format, ...) {} +inline void FailureMessage(const char *format, ...) {} +#endif // DEBUG + + +// Forward declaration. +class Vertex; + + +//------------------------------------------------------------------------------ +// The Trapezoid (actually, up to two of them) is embedded into a Vertex, whose +// point() provides the top of the Trapezoid, whereas the bottom, and the left +// and right edges, are stored in the Trapezoid. The edges are represented by +// their tail point; the head is the successive point modulo the number of +// points in the polygon. Only the Y coordinate of the top and bottom are +// relevant. +//------------------------------------------------------------------------------ +class Trapezoid { +public: + const Vertex* left() const { return fLeft; } + const Vertex* right() const { return fRight; } + const Vertex* bottom() const { return fBottom; } + Vertex* left() { return fLeft; } + Vertex* right() { return fRight; } + Vertex* bottom() { return fBottom; } + void setLeft(Vertex *left) { fLeft = left; } + void setRight(Vertex *right) { fRight = right; } + void setBottom(Vertex *bottom) { fBottom = bottom; } + void nullify() { setBottom(NULL); } + + bool operator<(Trapezoid &t1) { return compare(t1) < 0; } + bool operator>(Trapezoid &t1) { return compare(t1) > 0; } + +private: + Vertex *fLeft, *fRight, *fBottom; + + // These return a number that is less than, equal to, or greater than zero + // depending on whether the trapezoid or point is to the left, on, or to the + // right. + SkScalar compare(const Trapezoid &t1) const; + SkScalar compare(const SkPoint &p) const; +}; + + +//------------------------------------------------------------------------------ +// The ActiveTrapezoids are a sorted list containing the currently active +// trapezoids, i.e. those that have the top, left, and right, but still need the +// bottom. This could use some optimization, to reduce search time from O(n) to +// O(log n). +//------------------------------------------------------------------------------ +class ActiveTrapezoids { +public: + ActiveTrapezoids() { fTrapezoids.setCount(0); } + + size_t count() const { return fTrapezoids.count(); } + + // Select an unused trapezoid from the Vertex vt, initialize it with the + // left and right edges, and insert it into the sorted list. + bool insertNewTrapezoid(Vertex *vt, Vertex *left, Vertex *right); + + // Remove the specified Trapezoids from the active list. + void remove(Trapezoid *t); + + // Determine whether the given point lies within any active trapezoid, and + // return a pointer to that Trapezoid. + bool withinActiveTrapezoid(const SkPoint &pt, Trapezoid **tp); + + // Find an active trapezoid that contains the given edge. + Trapezoid* getTrapezoidWithEdge(const Vertex *edge); + +private: + // Insert the specified Trapezoid into the sorted list. + void insert(Trapezoid *t); + + // The sorted list of active trapezoids. This is O(n), and would benefit + // a 2-3 tree o achieve O(log n). + SkTDArray<Trapezoid*> fTrapezoids; // Fournier suggests a 2-3 tree instead. +}; + + +//------------------------------------------------------------------------------ +// The Vertex is used to communicate information between the various phases of +// triangulation. +//------------------------------------------------------------------------------ +class Vertex { +public: + enum VertexType { MONOTONE, CONVEX, CONCAVE }; + + Trapezoid fTrap0; + Trapezoid fTrap1; + + const SkPoint &point() const { return fPoint; } + void setPoint(const SkPoint &point) { fPoint = point; } + + // The next and previous vertices around the polygon. + Vertex *next() { return fNext; } + Vertex *prev() { return fPrev; } + const Vertex *next() const { return fNext; } + const Vertex *prev() const { return fPrev; } + void setNext(Vertex *next) { fNext = next; } + void setPrev(Vertex *prev) { fPrev = prev; } + + void setDone(bool done) { fDone = done; } + bool done() const { return fDone; } + + // Trapezoid accessors return non-null for any complete trapezoids. + void trapezoids(Trapezoid **trap0, Trapezoid **trap1) { + *trap0 = (fTrap0.bottom() != NULL) ? &fTrap0 : NULL; + *trap1 = (fTrap1.bottom() != NULL) ? &fTrap1 : NULL; + } + + // Eliminate a trapezoid. + void nullifyTrapezoid() { + if (fTrap1.bottom() != NULL) { + DebugPrintf("Unexpected non-null second trapezoid.\n"); + fTrap1.nullify(); + return; + } + fTrap0.nullify(); + } + + // Determine whether the edge specified by this Vertex shares the given top + // and bottom. + bool shareEdge(Vertex *top, Vertex *bottom); + + // Determines whether the angle specified by { prev, this, next } is convex. + // Note that collinear is considered to be convex. + bool angleIsConvex(); + + // Remove this Vertex from the { prev, next } linked list. + void delink() { + Vertex *p = prev(), + *n = next(); + p->setNext(n); + n->setPrev(p); + } + + // Return a number that is less than, equal to, or greater than zero + // depending on whether the point is to the left, on, or to the right of the + // edge that has this Vertex as a base. + SkScalar compare(const SkPoint &pt) const; + + // Classify the vertex, and return its sorted edges. + VertexType classify(Vertex **e0, Vertex **e1); + + // This helps determine unimonotonicity. + Vertex *diagonal(); + +private: + SkPoint fPoint; + Vertex *fNext; + Vertex *fPrev; + bool fDone; +}; + + +bool Vertex::angleIsConvex() { + SkPoint vPrev = prev()->point() - point(), + vNext = next()->point() - point(); + // TODO(turk): There might be overflow in fixed-point. + return SkPoint::CrossProduct(vNext, vPrev) >= 0; +} + + +bool Vertex::shareEdge(Vertex *top, Vertex *bottom) { + return (((this == top) && (this->next() == bottom)) || + ((this == bottom) && (this->next() == top))); +} + + +SkScalar Vertex::compare(const SkPoint &pt) const { + SkPoint ve = next()->point() - point(), + vp = pt - point(); + SkScalar d; + if (ve.fY == 0) { + // Return twice the distance to the center of the horizontal edge. + d = ve.fX + pt.fX - next()->point().fX; + } else { + // Return the distance to the edge, scaled by the edge length. + d = SkPoint::CrossProduct(ve, vp); + if (ve.fY > 0) d = -d; + } + return d; +} + + +SkScalar Trapezoid::compare(const SkPoint &pt) const { + SkScalar d = left()->compare(pt); + if (d <= 0) + return d; // Left of the left edge. + d = right()->compare(pt); + if (d >= 0) + return d; // Right of the right edge. + return 0; // Somewhere between the left and the right edges. +} + + + +SkScalar Trapezoid::compare(const Trapezoid &t1) const { +#if 1 + SkScalar d = left()->compare(t1.left()->point()); + if (d == 0) + d = right()->compare(t1.right()->point()); + return d; +#else + SkScalar dl = left()->compare( t1.left()->point()), + dr = right()->compare(t1.right()->point()); + if (dl < 0 && dr < 0) + return dr; + if (dl > 0 && dr > 0) + return dl; + return 0; +#endif +} + + +Trapezoid* ActiveTrapezoids::getTrapezoidWithEdge(const Vertex *edge) { + DebugPrintf("Entering getTrapezoidWithEdge(): looking through %d\n", + fTrapezoids.count()); + DebugPrintf("trying to find %p: ", edge); + Trapezoid **tp; + for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) { + SkASSERT(tp != NULL); + SkASSERT(*tp != NULL); + DebugPrintf("%p and %p, ", (**tp).left(), (**tp).right()); + if ((**tp).left() == edge || (**tp).right() == edge) { + DebugPrintf("\ngetTrapezoidWithEdge found the trapezoid\n"); + return *tp; + } + } + DebugPrintf("getTrapezoidWithEdge found no trapezoid\n"); + return NULL; +} + + +bool ActiveTrapezoids::insertNewTrapezoid(Vertex *vt, + Vertex *left, + Vertex *right) { + DebugPrintf("Inserting a trapezoid..."); + if (vt->fTrap0.left() == NULL && vt->fTrap0.right() == NULL) { + vt->fTrap0.setLeft(left); + vt->fTrap0.setRight(right); + insert(&vt->fTrap0); + } else if (vt->fTrap1.left() == NULL && vt->fTrap1.right() == NULL) { + DebugPrintf("a second one..."); + vt->fTrap1.setLeft(left); + vt->fTrap1.setRight(right); + if (vt->fTrap1 < vt->fTrap0) { // Keep trapezoids sorted. + remove(&vt->fTrap0); + Trapezoid t = vt->fTrap0; + vt->fTrap0 = vt->fTrap1; + vt->fTrap1 = t; + insert(&vt->fTrap0); + } + insert(&vt->fTrap1); + } else { + FailureMessage("More than 2 trapezoids requested for a vertex\n"); + return false; + } + DebugPrintf(" done. %d incomplete trapezoids\n", fTrapezoids.count()); + return true; +} + + +void ActiveTrapezoids::insert(Trapezoid *t) { + Trapezoid **tp; + for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) + if (**tp > *t) + break; + fTrapezoids.insert(tp - fTrapezoids.begin(), 1, &t); + // SHOULD VERIFY THAT ALL TRAPEZOIDS ARE PROPERLY SORTED +} + + +void ActiveTrapezoids::remove(Trapezoid *t) { + DebugPrintf("Removing a trapezoid..."); + for (Trapezoid **tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) { + if (*tp == t) { + fTrapezoids.remove(tp - fTrapezoids.begin()); + DebugPrintf(" done.\n"); + return; + } + } + DebugPrintf(" Arghh! Panic!\n"); + SkASSERT(t == 0); // Cannot find t in active trapezoid list. +} + + +bool ActiveTrapezoids::withinActiveTrapezoid(const SkPoint &pt, + Trapezoid **trap) { + DebugPrintf("Entering withinActiveTrapezoid()\n"); + // This is where a good search data structure would be helpful. + Trapezoid **t; + for (t = fTrapezoids.begin(); t < fTrapezoids.end(); ++t) { + if ((**t).left()->compare(pt) <= 0) { + // The point is to the left of the left edge of this trapezoid. + DebugPrintf("withinActiveTrapezoid: Before a trapezoid\n"); + *trap = *t; // Return the place where a new trapezoid would go. +// We have a bug with the sorting -- look through everything. + continue; +// return false; // Outside all trapezoids, since they are sorted. + } + // The point is to the right of the left edge of this trapezoid. + + if ((**t).right()->compare(pt) < 0) { + // The point is to the left of the right edge. + DebugPrintf("withinActiveTrapezoid: Within an Active Trapezoid\n"); + *trap = *t; + return true; + } + } + + // The point is to the right of all trapezoids. + DebugPrintf("withinActiveTrapezoid: After all trapezoids\n"); + *trap = NULL; + return false; +} + + +Vertex* Vertex::diagonal() { + Vertex *diag = NULL; + if (fTrap0.bottom() != NULL) { + if (!fTrap0.left() ->shareEdge(this, fTrap0.bottom()) && + !fTrap0.right()->shareEdge(this, fTrap0.bottom()) + ) { + diag = fTrap0.bottom(); + fTrap0 = fTrap1; + fTrap1.nullify(); + } else if (fTrap1.bottom() != NULL && + !fTrap1.left() ->shareEdge(this, fTrap1.bottom()) && + !fTrap1.right()->shareEdge(this, fTrap1.bottom()) + ) { + diag = fTrap1.bottom(); + fTrap1.nullify(); + } + } + return diag; +} + + +// We use this to sort the edges vertically for a scan-conversion type of +// operation. +class VertexPtr { +public: + Vertex *vt; +}; + + +bool operator<(VertexPtr &v0, VertexPtr &v1) { + // DebugPrintf("< %p %p\n", &v0, &v1); + if (v0.vt->point().fY < v1.vt->point().fY) return true; + if (v0.vt->point().fY > v1.vt->point().fY) return false; + if (v0.vt->point().fX < v1.vt->point().fX) return true; + else return false; +} + + +bool operator>(VertexPtr &v0, VertexPtr &v1) { + // DebugPrintf("> %p %p\n", &v0, &v1); + if (v0.vt->point().fY > v1.vt->point().fY) return true; + if (v0.vt->point().fY < v1.vt->point().fY) return false; + if (v0.vt->point().fX > v1.vt->point().fX) return true; + else return false; +} + + +static void SetVertexPoints(size_t numPts, const SkPoint *pt, Vertex *vt) { + for (; numPts-- != 0; ++pt, ++vt) + vt->setPoint(*pt); +} + + +static void InitializeVertexTopology(size_t numPts, Vertex *v1) { + Vertex *v0 = v1 + numPts - 1, *v_1 = v0 - 1; + for (; numPts-- != 0; v_1 = v0, v0 = v1++) { + v0->setPrev(v_1); + v0->setNext(v1); + } +} + +Vertex::VertexType Vertex::classify(Vertex **e0, Vertex **e1) { + VertexType type; + SkPoint vPrev, vNext; + vPrev.fX = prev()->point().fX - point().fX; + vPrev.fY = prev()->point().fY - point().fY; + vNext.fX = next()->point().fX - point().fX; + vNext.fY = next()->point().fY - point().fY; + + // This can probably be simplified, but there are enough potential bugs, + // we will leave it expanded until all cases are tested appropriately. + if (vPrev.fY < 0) { + if (vNext.fY > 0) { + // Prev comes from above, Next goes below. + type = MONOTONE; + *e0 = prev(); + *e1 = this; + } else if (vNext.fY < 0) { + // The are both above: sort so that e0 is on the left. + type = CONCAVE; + if (SkPoint::CrossProduct(vPrev, vNext) <= 0) { + *e0 = this; + *e1 = prev(); + } else { + *e0 = prev(); + *e1 = this; + } + } else { + DebugPrintf("### py < 0, ny = 0\n"); + if (vNext.fX < 0) { + type = CONCAVE; + *e0 = this; // flat to the left + *e1 = prev(); // concave on the right + } else { + type = CONCAVE; + *e0 = prev(); // concave to the left + *e1 = this; // flat to the right + } + } + } else if (vPrev.fY > 0) { + if (vNext.fY < 0) { + // Next comes from above, Prev goes below. + type = MONOTONE; + *e0 = this; + *e1 = prev(); + } else if (vNext.fY > 0) { + // They are both below: sort so that e0 is on the left. + type = CONVEX; + if (SkPoint::CrossProduct(vPrev, vNext) <= 0) { + *e0 = prev(); + *e1 = this; + } else { + *e0 = this; + *e1 = prev(); + } + } else { + DebugPrintf("### py > 0, ny = 0\n"); + if (vNext.fX < 0) { + type = MONOTONE; + *e0 = this; // flat to the left + *e1 = prev(); // convex on the right - try monotone first + } else { + type = MONOTONE; + *e0 = prev(); // convex to the left - try monotone first + *e1 = this; // flat to the right + } + } + } else { // vPrev.fY == 0 + if (vNext.fY < 0) { + DebugPrintf("### py = 0, ny < 0\n"); + if (vPrev.fX < 0) { + type = CONCAVE; + *e0 = prev(); // flat to the left + *e1 = this; // concave on the right + } else { + type = CONCAVE; + *e0 = this; // concave on the left - defer + *e1 = prev(); // flat to the right + } + } else if (vNext.fY > 0) { + DebugPrintf("### py = 0, ny > 0\n"); + if (vPrev.fX < 0) { + type = MONOTONE; + *e0 = prev(); // flat to the left + *e1 = this; // convex on the right - try monotone first + } else { + type = MONOTONE; + *e0 = this; // convex to the left - try monotone first + *e1 = prev(); // flat to the right + } + } else { + DebugPrintf("### py = 0, ny = 0\n"); + // First we try concave, then monotone, then convex. + if (vPrev.fX <= vNext.fX) { + type = CONCAVE; + *e0 = prev(); // flat to the left + *e1 = this; // flat to the right + } else { + type = CONCAVE; + *e0 = this; // flat to the left + *e1 = prev(); // flat to the right + } + } + } + return type; +} + + +#ifdef DEBUG +static const char* GetVertexTypeString(Vertex::VertexType type) { + const char *typeStr = NULL; + switch (type) { + case Vertex::MONOTONE: + typeStr = "MONOTONE"; + break; + case Vertex::CONCAVE: + typeStr = "CONCAVE"; + break; + case Vertex::CONVEX: + typeStr = "CONVEX"; + break; + } + return typeStr; +} + + +static void PrintVertices(size_t numPts, Vertex *vt) { + DebugPrintf("\nVertices:\n"); + for (size_t i = 0; i < numPts; i++) { + Vertex *e0, *e1; + Vertex::VertexType type = vt[i].classify(&e0, &e1); + DebugPrintf("%2d: (%.7g, %.7g), prev(%d), next(%d), " + "type(%s), left(%d), right(%d)", + i, vt[i].point().fX, vt[i].point().fY, + vt[i].prev() - vt, vt[i].next() - vt, + GetVertexTypeString(type), e0 - vt, e1 - vt); + Trapezoid *trap[2]; + vt[i].trapezoids(trap, trap+1); + for (int j = 0; j < 2; ++j) { + if (trap[j] != NULL) { + DebugPrintf(", trap(L=%d, R=%d, B=%d)", + trap[j]->left() - vt, + trap[j]->right() - vt, + trap[j]->bottom() - vt); + } + } + DebugPrintf("\n"); + } +} + + +static void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) { + DebugPrintf("\nSorted Vertices:\n"); + for (size_t i = 0; i < numPts; i++) { + Vertex *e0, *e1; + Vertex *vt = vp[i].vt; + Vertex::VertexType type = vt->classify(&e0, &e1); + DebugPrintf("%2d: %2d: (%.7g, %.7g), prev(%d), next(%d), " + "type(%s), left(%d), right(%d)", + i, vt - vtBase, vt->point().fX, vt->point().fY, + vt->prev() - vtBase, vt->next() - vtBase, + GetVertexTypeString(type), e0 - vtBase, e1 - vtBase); + Trapezoid *trap[2]; + vt->trapezoids(trap, trap+1); + for (int j = 0; j < 2; ++j) { + if (trap[j] != NULL) { + DebugPrintf(", trap(L=%d, R=%d, B=%d)", + trap[j]->left() - vtBase, + trap[j]->right() - vtBase, + trap[j]->bottom() - vtBase); + } + } + DebugPrintf("\n"); + } +} +#else // !DEBUG +inline void PrintVertices(size_t numPts, Vertex *vt) {} +inline void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) {} +#endif // !DEBUG + + +// SkTHeapSort is broken, so we use a bubble sort in the meantime. +template <typename T> +void BubbleSort(T *array, size_t count) { + bool sorted; + size_t count_1 = count - 1; + do { + sorted = true; + for (size_t i = 0; i < count_1; ++i) { + if (array[i + 1] < array[i]) { + T t = array[i]; + array[i] = array[i + 1]; + array[i + 1] = t; + sorted = false; + } + } + } while (!sorted); +} + + +// Remove zero-height trapezoids. +static void RemoveDegenerateTrapezoids(size_t numVt, Vertex *vt) { + for (; numVt-- != 0; vt++) { + Trapezoid *traps[2]; + vt->trapezoids(traps, traps+1); + if (traps[1] != NULL && + vt->point().fY >= traps[1]->bottom()->point().fY) { + traps[1]->nullify(); + traps[1] = NULL; + } + if (traps[0] != NULL && + vt->point().fY >= traps[0]->bottom()->point().fY) { + if (traps[1] != NULL) { + *traps[0] = *traps[1]; + traps[1]->nullify(); + } else { + traps[0]->nullify(); + } + } + } +} + + +// Enhance the polygon with trapezoids. +bool ConvertPointsToVertices(size_t numPts, const SkPoint *pts, Vertex *vta) { + DebugPrintf("ConvertPointsToVertices()\n"); + + // Clear everything. + DebugPrintf("Zeroing vertices\n"); + sk_bzero(vta, numPts * sizeof(*vta)); + + // Initialize vertices. + DebugPrintf("Initializing vertices\n"); + SetVertexPoints(numPts, pts, vta); + InitializeVertexTopology(numPts, vta); + + PrintVertices(numPts, vta); + + SkTDArray<VertexPtr> vtptr; + vtptr.setCount(numPts); + for (int i = numPts; i-- != 0;) + vtptr[i].vt = vta + i; + PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta); + DebugPrintf("Sorting vertrap ptr array [%d] %p %p\n", vtptr.count(), + &vtptr[0], &vtptr[vtptr.count() - 1] + ); +// SkTHeapSort(vtptr.begin(), vtptr.count()); + BubbleSort(vtptr.begin(), vtptr.count()); + DebugPrintf("Done sorting\n"); + PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta); + + DebugPrintf("Traversing sorted vertrap ptrs\n"); + ActiveTrapezoids incompleteTrapezoids; + for (VertexPtr *vtpp = vtptr.begin(); vtpp < vtptr.end(); ++vtpp) { + DebugPrintf("%d: sorted vertrap %d\n", + vtpp - vtptr.begin(), vtpp->vt - vta); + Vertex *vt = vtpp->vt; + Vertex *e0, *e1; + Trapezoid *t; + switch (vt->classify(&e0, &e1)) { + case Vertex::MONOTONE: + monotone: + DebugPrintf("MONOTONE %d %d\n", e0 - vta, e1 - vta); + // We should find one edge. + t = incompleteTrapezoids.getTrapezoidWithEdge(e0); + if (t == NULL) { // One of the edges is flat. + DebugPrintf("Monotone: cannot find a trapezoid with e0: " + "trying convex\n"); + goto convex; + } + t->setBottom(vt); // This trapezoid is now complete. + incompleteTrapezoids.remove(t); + + if (e0 == t->left()) // Replace the left edge. + incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right()); + else // Replace the right edge. + incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e1); + break; + + case Vertex::CONVEX: // Start of a new trapezoid. + convex: + // We don't expect to find any edges. + DebugPrintf("CONVEX %d %d\n", e0 - vta, e1 - vta); + if (incompleteTrapezoids.withinActiveTrapezoid( + vt->point(), &t)) { + // Complete trapezoid. + SkASSERT(t != NULL); + t->setBottom(vt); + incompleteTrapezoids.remove(t); + // Introduce two new trapezoids. + incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e0); + incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right()); + } else { + // Insert a new trapezoid. + incompleteTrapezoids.insertNewTrapezoid(vt, e0, e1); + } + break; + + case Vertex::CONCAVE: // End of a trapezoid. + DebugPrintf("CONCAVE %d %d\n", e0 - vta, e1 - vta); + // We should find two edges. + t = incompleteTrapezoids.getTrapezoidWithEdge(e0); + if (t == NULL) { + DebugPrintf("Concave: cannot find a trapezoid with e0: " + " trying monotone\n"); + goto monotone; + } + SkASSERT(t != NULL); + if (e0 == t->left() && e1 == t->right()) { + DebugPrintf( + "Concave edges belong to the same trapezoid.\n"); + // Edges belong to the same trapezoid. + // Complete trapezoid & transfer it from the active list. + t->setBottom(vt); + incompleteTrapezoids.remove(t); + } else { // Edges belong to different trapezoids + DebugPrintf( + "Concave edges belong to different trapezoids.\n"); + // Complete left and right trapezoids. + Trapezoid *s = incompleteTrapezoids.getTrapezoidWithEdge( + e1); + if (s == NULL) { + DebugPrintf( + "Concave: cannot find a trapezoid with e1: " + " trying monotone\n"); + goto monotone; + } + t->setBottom(vt); + s->setBottom(vt); + incompleteTrapezoids.remove(t); + incompleteTrapezoids.remove(s); + + // Merge the two trapezoids into one below this vertex. + incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), + s->right()); + } + break; + } + } + + RemoveDegenerateTrapezoids(numPts, vta); + + DebugPrintf("Done making trapezoids\n"); + PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta); + + size_t k = incompleteTrapezoids.count(); + if (k > 0) { + FailureMessage("%d incomplete trapezoids\n", k); + return false; + } + return true; +} + + +inline void appendTriangleAtVertex(const Vertex *v, + SkTDArray<SkPoint> *triangles) { + SkPoint *p = triangles->append(3); + p[0] = v->prev()->point(); + p[1] = v->point(); + p[2] = v->next()->point(); + DebugPrintf( + "Appending triangle: { (%.7g, %.7g), (%.7g, %.7g), (%.7g, %.7g) }\n", + p[0].fX, p[0].fY, + p[1].fX, p[1].fY, + p[2].fX, p[2].fY + ); +} + + +static size_t CountVertices(const Vertex *first, const Vertex *last) { + DebugPrintf("Counting vertices: "); + size_t count = 1; + for (; first != last; first = first->next()) { + ++count; + SkASSERT(count <= kMaxCount); + if (count >= kMaxCount) { + FailureMessage("Vertices do not seem to be in a linked chain\n"); + break; + } + } + return count; +} + + +bool operator<(const SkPoint &p0, const SkPoint &p1) { + if (p0.fY < p1.fY) return true; + if (p0.fY > p1.fY) return false; + if (p0.fX < p1.fX) return true; + else return false; +} + + +static void PrintLinkedVertices(size_t n, Vertex *vertices) { + DebugPrintf("%d vertices:\n", n); + Vertex *v; + for (v = vertices; n-- != 0; v = v->next()) + DebugPrintf(" (%.7g, %.7g)\n", v->point().fX, v->point().fY); + if (v != vertices) + FailureMessage("Vertices are not in a linked chain\n"); +} + + +// Triangulate an unimonotone chain. +bool TriangulateMonotone(Vertex *first, Vertex *last, + SkTDArray<SkPoint> *triangles) { + DebugPrintf("TriangulateMonotone()\n"); + + size_t numVertices = CountVertices(first, last); + if (numVertices == kMaxCount) { + FailureMessage("Way too many vertices: %d:\n", numVertices); + PrintLinkedVertices(numVertices, first); + return false; + } + + Vertex *start = first; + // First find the point with the smallest Y. + DebugPrintf("TriangulateMonotone: finding bottom\n"); + int count = kMaxCount; // Maximum number of vertices. + for (Vertex *v = first->next(); v != first && count-- > 0; v = v->next()) + if (v->point() < start->point()) + start = v; + if (count <= 0) { + FailureMessage("TriangulateMonotone() was given disjoint chain\n"); + return false; // Something went wrong. + } + + // Start at the far end of the long edge. + if (start->prev()->point() < start->next()->point()) + start = start->next(); + + Vertex *current = start->next(); + while (numVertices >= 3) { + if (current->angleIsConvex()) { + DebugPrintf("Angle %p is convex\n", current); + // Print the vertices + PrintLinkedVertices(numVertices, start); + + appendTriangleAtVertex(current, triangles); + if (triangles->count() > kMaxCount * 3) { + FailureMessage("An extraordinarily large number of triangles " + "were generated\n"); + return false; + } + Vertex *save = current->prev(); + current->delink(); + current = (save == start || save == start->prev()) ? start->next() + : save; + --numVertices; + } else { + if (numVertices == 3) { + FailureMessage("Convexity error in TriangulateMonotone()\n"); + appendTriangleAtVertex(current, triangles); + return false; + } + DebugPrintf("Angle %p is concave\n", current); + current = current->next(); + } + } + return true; +} + + +// Split the polygon into sets of unimonotone chains, and eventually call +// TriangulateMonotone() to convert them into triangles. +bool Triangulate(Vertex *first, Vertex *last, SkTDArray<SkPoint> *triangles) { + DebugPrintf("Triangulate()\n"); + Vertex *currentVertex = first; + while (!currentVertex->done()) { + currentVertex->setDone(true); + Vertex *bottomVertex = currentVertex->diagonal(); + if (bottomVertex != NULL) { + Vertex *saveNext = currentVertex->next(); + Vertex *savePrev = bottomVertex->prev(); + currentVertex->setNext(bottomVertex); + bottomVertex->setPrev(currentVertex); + currentVertex->nullifyTrapezoid(); + bool success = Triangulate(bottomVertex, currentVertex, triangles); + currentVertex->setDone(false); + bottomVertex->setDone(false); + currentVertex->setNext(saveNext); + bottomVertex->setPrev(savePrev); + bottomVertex->setNext(currentVertex); + currentVertex->setPrev(bottomVertex); + return Triangulate(currentVertex, bottomVertex, triangles) + && success; + } else { + currentVertex = currentVertex->next(); + } + } + return TriangulateMonotone(first, last, triangles); +} + + +bool SkConcaveToTriangles(size_t numPts, + const SkPoint pts[], + SkTDArray<SkPoint> *triangles) { + DebugPrintf("SkConcaveToTriangles()\n"); + + SkTDArray<Vertex> vertices; + vertices.setCount(numPts); + if (!ConvertPointsToVertices(numPts, pts, vertices.begin())) + return false; + + triangles->setReserve(numPts); + triangles->setCount(0); + return Triangulate(vertices.begin(), vertices.end() - 1, triangles); +} diff --git a/src/core/SkConcaveToTriangles.h b/src/core/SkConcaveToTriangles.h new file mode 100644 index 0000000..cede026 --- /dev/null +++ b/src/core/SkConcaveToTriangles.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 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 SkConcaveToTriangles_DEFINED +#define SkConcaveToTriangles_DEFINED + +#include "SkPoint.h" +#include "SkTDArray.h" + + +// Triangulate a polygon. +// The polygon can be convex or concave, and can have holes or multiple contours +// of arbitrary recursion. +// The holes must have opposite orientation of the outer contours, whereas +// islands within the holes must have the same orientation as the outer contour. +// Contours should be joined by zero-thickness double-edges, to mimic a single +// polygon. The polygon should not be self-intersecting. +// Currently, the outer contour must be right-handed, i.e. it should be oriented +// in the direction that rotates the X-axis to the Y-axis. +bool SkConcaveToTriangles(size_t count, + const SkPoint pts[], + SkTDArray<SkPoint> *triangles); + + +#endif // SkConcaveToTriangles_DEFINED diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h index 6204b2c..32e8035 100644 --- a/src/core/SkCoreBlitters.h +++ b/src/core/SkCoreBlitters.h @@ -101,7 +101,8 @@ public: virtual const SkBitmap* justAnOpaqueColor(uint32_t*); protected: - SkColor fPMColor; + SkColor fPMColor; + SkBlitRow::ColorProc fColor32Proc; private: unsigned fSrcA, fSrcR, fSrcG, fSrcB; diff --git a/src/core/SkCubicClipper.cpp b/src/core/SkCubicClipper.cpp new file mode 100644 index 0000000..6e47513 --- /dev/null +++ b/src/core/SkCubicClipper.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2009 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 "SkCubicClipper.h" +#include "SkGeometry.h" + +SkCubicClipper::SkCubicClipper() {} + +void SkCubicClipper::setClip(const SkIRect& clip) { + // conver to scalars, since that's where we'll see the points + fClip.set(clip); +} + + +static bool chopMonoCubicAtY(SkPoint pts[4], SkScalar y, SkScalar* t) { + SkScalar ycrv[4]; + ycrv[0] = pts[0].fY - y; + ycrv[1] = pts[1].fY - y; + ycrv[2] = pts[2].fY - y; + ycrv[3] = pts[3].fY - y; + +#ifdef NEWTON_RAPHSON // Quadratic convergence, typically <= 3 iterations. + // Initial guess. + // TODO(turk): Check for zero denominator? Shouldn't happen unless the curve + // is not only monotonic but degenerate. +#ifdef SK_SCALAR_IS_FLOAT + SkScalar t1 = ycrv[0] / (ycrv[0] - ycrv[3]); +#else // !SK_SCALAR_IS_FLOAT + SkScalar t1 = SkDivBits(ycrv[0], ycrv[0] - ycrv[3], 16); +#endif // !SK_SCALAR_IS_FLOAT + + // Newton's iterations. + const SkScalar tol = SK_Scalar1 / 16384; // This leaves 2 fixed noise bits. + SkScalar t0; + const int maxiters = 5; + int iters = 0; + bool converged; + do { + t0 = t1; + SkScalar y01 = SkScalarInterp(ycrv[0], ycrv[1], t0); + SkScalar y12 = SkScalarInterp(ycrv[1], ycrv[2], t0); + SkScalar y23 = SkScalarInterp(ycrv[2], ycrv[3], t0); + SkScalar y012 = SkScalarInterp(y01, y12, t0); + SkScalar y123 = SkScalarInterp(y12, y23, t0); + SkScalar y0123 = SkScalarInterp(y012, y123, t0); + SkScalar yder = (y123 - y012) * 3; + // TODO(turk): check for yder==0: horizontal. +#ifdef SK_SCALAR_IS_FLOAT + t1 -= y0123 / yder; +#else // !SK_SCALAR_IS_FLOAT + t1 -= SkDivBits(y0123, yder, 16); +#endif // !SK_SCALAR_IS_FLOAT + converged = SkScalarAbs(t1 - t0) <= tol; // NaN-safe + ++iters; + } while (!converged && (iters < maxiters)); + *t = t1; // Return the result. + + // The result might be valid, even if outside of the range [0, 1], but + // we never evaluate a Bezier outside this interval, so we return false. + if (t1 < 0 || t1 > SK_Scalar1) + return false; // This shouldn't happen, but check anyway. + return converged; + +#else // BISECTION // Linear convergence, typically 16 iterations. + + // Check that the endpoints straddle zero. + SkScalar tNeg, tPos; // Negative and positive function parameters. + if (ycrv[0] < 0) { + if (ycrv[3] < 0) + return false; + tNeg = 0; + tPos = SK_Scalar1; + } else if (ycrv[0] > 0) { + if (ycrv[3] > 0) + return false; + tNeg = SK_Scalar1; + tPos = 0; + } else { + *t = 0; + return true; + } + + const SkScalar tol = SK_Scalar1 / 65536; // 1 for fixed, 1e-5 for float. + int iters = 0; + do { + SkScalar tMid = (tPos + tNeg) / 2; + SkScalar y01 = SkScalarInterp(ycrv[0], ycrv[1], tMid); + SkScalar y12 = SkScalarInterp(ycrv[1], ycrv[2], tMid); + SkScalar y23 = SkScalarInterp(ycrv[2], ycrv[3], tMid); + SkScalar y012 = SkScalarInterp(y01, y12, tMid); + SkScalar y123 = SkScalarInterp(y12, y23, tMid); + SkScalar y0123 = SkScalarInterp(y012, y123, tMid); + if (y0123 == 0) { + *t = tMid; + return true; + } + if (y0123 < 0) tNeg = tMid; + else tPos = tMid; + ++iters; + } while (!(SkScalarAbs(tPos - tNeg) <= tol)); // Nan-safe + + *t = (tNeg + tPos) / 2; + return true; +#endif // BISECTION +} + + +bool SkCubicClipper::clipCubic(const SkPoint srcPts[4], SkPoint dst[4]) { + bool reverse; + + // we need the data to be monotonically descending in Y + if (srcPts[0].fY > srcPts[3].fY) { + dst[0] = srcPts[3]; + dst[1] = srcPts[2]; + dst[2] = srcPts[1]; + dst[3] = srcPts[0]; + reverse = true; + } else { + memcpy(dst, srcPts, 4 * sizeof(SkPoint)); + reverse = false; + } + + // are we completely above or below + const SkScalar ctop = fClip.fTop; + const SkScalar cbot = fClip.fBottom; + if (dst[3].fY <= ctop || dst[0].fY >= cbot) { + return false; + } + + SkScalar t; + SkPoint tmp[7]; // for SkChopCubicAt + + // are we partially above + if (dst[0].fY < ctop && chopMonoCubicAtY(dst, ctop, &t)) { + SkChopCubicAt(dst, tmp, t); + dst[0] = tmp[3]; + dst[1] = tmp[4]; + dst[2] = tmp[5]; + } + + // are we partially below + if (dst[3].fY > cbot && chopMonoCubicAtY(dst, cbot, &t)) { + SkChopCubicAt(dst, tmp, t); + dst[1] = tmp[1]; + dst[2] = tmp[2]; + dst[3] = tmp[3]; + } + + if (reverse) { + SkTSwap<SkPoint>(dst[0], dst[3]); + SkTSwap<SkPoint>(dst[1], dst[2]); + } + return true; +} + diff --git a/src/core/SkCubicClipper.h b/src/core/SkCubicClipper.h new file mode 100644 index 0000000..9a45b31 --- /dev/null +++ b/src/core/SkCubicClipper.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 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 SkCubicClipper_DEFINED +#define SkCubicClipper_DEFINED + +#include "SkPoint.h" +#include "SkRect.h" + +/** This class is initialized with a clip rectangle, and then can be fed cubics, + which must already be monotonic in Y. + + In the future, it might return a series of segments, allowing it to clip + also in X, to ensure that all segments fit in a finite coordinate system. + */ +class SkCubicClipper { +public: + SkCubicClipper(); + + void setClip(const SkIRect& clip); + + bool clipCubic(const SkPoint src[4], SkPoint dst[4]); + +private: + SkRect fClip; +}; + +#endif // SkCubicClipper_DEFINED diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index b629e9a..a270677 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -4,9 +4,20 @@ SkDeviceFactory::~SkDeviceFactory() {} -SkDevice::SkDevice() {} - -SkDevice::SkDevice(const SkBitmap& bitmap) : fBitmap(bitmap) {} +SkDevice::SkDevice(SkCanvas* canvas) : fCanvas(canvas) {} + +SkDevice::SkDevice(SkCanvas* canvas, const SkBitmap& bitmap, bool isForLayer) + : fCanvas(canvas), fBitmap(bitmap) { + // auto-allocate if we're for offscreen drawing + if (isForLayer) { + if (NULL == fBitmap.getPixels() && NULL == fBitmap.pixelRef()) { + fBitmap.allocPixels(); + if (!fBitmap.isOpaque()) { + fBitmap.eraseColor(0); + } + } + } +} void SkDevice::lockPixels() { fBitmap.lockPixels(); @@ -47,6 +58,39 @@ void SkDevice::setMatrixClip(const SkMatrix&, const SkRegion&) {} /////////////////////////////////////////////////////////////////////////////// +bool SkDevice::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) { + const SkBitmap& src = this->accessBitmap(false); + + SkIRect bounds; + bounds.set(0, 0, src.width(), src.height()); + if (!bounds.intersect(srcRect)) { + return false; + } + + SkBitmap subset; + if (!src.extractSubset(&subset, bounds)) { + return false; + } + + SkBitmap tmp; + if (!subset.copyTo(&tmp, SkBitmap::kARGB_8888_Config)) { + return false; + } + + tmp.swap(*bitmap); + return true; +} + +void SkDevice::writePixels(const SkBitmap& bitmap, int x, int y) { + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + + SkCanvas canvas(this); + canvas.drawSprite(bitmap, x, y, &paint); +} + +/////////////////////////////////////////////////////////////////////////////// + void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { draw.drawPaint(paint); } @@ -62,13 +106,24 @@ void SkDevice::drawRect(const SkDraw& draw, const SkRect& r, } void SkDevice::drawPath(const SkDraw& draw, const SkPath& path, - const SkPaint& paint) { - draw.drawPath(path, paint); + const SkPaint& paint, const SkMatrix* prePathMatrix, + bool pathIsMutable) { + draw.drawPath(path, paint, prePathMatrix, pathIsMutable); } void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, - const SkMatrix& matrix, const SkPaint& paint) { - draw.drawBitmap(bitmap, matrix, paint); + const SkIRect* srcRect, + const SkMatrix& matrix, const SkPaint& paint) { + SkBitmap tmp; // storage if we need a subset of bitmap + const SkBitmap* bitmapPtr = &bitmap; + + if (srcRect) { + if (!bitmap.extractSubset(&tmp, *srcRect)) { + return; // extraction failed + } + bitmapPtr = &tmp; + } + draw.drawBitmap(*bitmapPtr, matrix, paint); } void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, @@ -115,16 +170,15 @@ void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device, draw.drawSprite(device->accessBitmap(false), x, y, paint); } -SkDevice* SkRasterDeviceFactory::newDevice(SkBitmap::Config config, int width, +/////////////////////////////////////////////////////////////////////////////// + +SkDevice* SkRasterDeviceFactory::newDevice(SkCanvas* canvas, + SkBitmap::Config config, int width, int height, bool isOpaque, bool isForLayer) { SkBitmap bitmap; bitmap.setConfig(config, width, height); bitmap.setIsOpaque(isOpaque); - bitmap.allocPixels(); - if (!bitmap.isOpaque()) - bitmap.eraseARGB(0, 0, 0, 0); - - return SkNEW_ARGS(SkDevice, (bitmap)); + return SkNEW_ARGS(SkDevice, (canvas, bitmap, isForLayer)); } diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index fb5e045..05e249a 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -29,6 +29,7 @@ #include "SkShader.h" #include "SkStroke.h" #include "SkTemplatesPriv.h" +#include "SkTextFormatParams.h" #include "SkUtils.h" #include "SkAutoKern.h" @@ -46,13 +47,13 @@ public: fDraw->fBounder = fBounder; } } - + void clearBounder(const SkDraw* draw) { fDraw = const_cast<SkDraw*>(draw); fBounder = draw->fBounder; fDraw->fBounder = NULL; } - + private: SkDraw* fDraw; SkBounder* fBounder; @@ -75,6 +76,7 @@ public: fBlitter = SkBlitter::Choose(device, matrix, paint, fStorage, sizeof(fStorage)); } + ~SkAutoBlitterChoose(); SkBlitter* operator->() { return fBlitter; } @@ -98,16 +100,17 @@ public: SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint* paint) : fPaint((SkPaint*)paint) { fPrevShader = paint->getShader(); - fPrevShader->safeRef(); + SkSafeRef(fPrevShader); fPaint->setShader(SkShader::CreateBitmapShader( src, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, fStorage, sizeof(fStorage))); } + ~SkAutoBitmapShaderInstall() { SkShader* shader = fPaint->getShader(); fPaint->setShader(fPrevShader); - fPrevShader->safeUnref(); + SkSafeUnref(fPrevShader); if ((void*)shader == (void*)fStorage) { shader->~SkShader(); @@ -115,6 +118,7 @@ public: SkDELETE(shader); } } + private: SkPaint* fPaint; SkShader* fPrevShader; @@ -128,9 +132,11 @@ public: fStyle = paint.getStyle(); // record the old fPaint.setStyle(style); // change it to the specified style } + ~SkAutoPaintStyleRestore() { fPaint.setStyle(fStyle); // restore the old } + private: SkPaint& fPaint; SkPaint::Style fStyle; @@ -142,6 +148,10 @@ private: /////////////////////////////////////////////////////////////////////////////// +SkDraw::SkDraw() { + sk_bzero(this, sizeof(*this)); +} + SkDraw::SkDraw(const SkDraw& src) { memcpy(this, &src, sizeof(*this)); } @@ -181,9 +191,9 @@ static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap, if (!SkXfermode::IsMode(paint.getXfermode(), &mode)) { return NULL; } - + SkColor color = paint.getColor(); - + // collaps modes based on color... if (SkXfermode::kSrcOver_Mode == mode) { unsigned alpha = SkColorGetA(color); @@ -193,7 +203,7 @@ static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap, mode = SkXfermode::kSrc_Mode; } } - + switch (mode) { case SkXfermode::kClear_Mode: // SkDebugf("--- D_Clear_BitmapXferProc\n"); @@ -203,7 +213,7 @@ static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap, return D_Dst_BitmapXferProc; // ignore data case SkXfermode::kSrc_Mode: { /* - should I worry about dithering for the lower depths? + should I worry about dithering for the lower depths? */ SkPMColor pmc = SkPreMultiplyColor(color); switch (bitmap.config()) { @@ -286,7 +296,7 @@ void SkDraw::drawPaint(const SkPaint& paint) const { if (fBounder && !fBounder->doIRect(devRect)) { return; } - + /* If we don't have a shader (i.e. we're just a solid color) we may be faster to operate directly on the device bitmap, rather than invoking a blitter. Esp. true for xfermodes, which require a colorshader to be @@ -299,7 +309,7 @@ void SkDraw::drawPaint(const SkPaint& paint) const { if (D_Dst_BitmapXferProc == proc) { // nothing to do return; } - + SkRegion::Iterator iter(*fClip); while (!iter.done()) { CallBitmapXferProc(*fBitmap, iter.rect(), proc, procData); @@ -318,15 +328,15 @@ struct PtProcRec { SkCanvas::PointMode fMode; const SkPaint* fPaint; const SkRegion* fClip; - + // computed values SkFixed fRadius; - + typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count, SkBlitter*); bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix, - const SkRegion* clip); + const SkRegion* clip); Proc chooseProc(SkBlitter* blitter); }; @@ -334,7 +344,7 @@ static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[], int count, SkBlitter* blitter) { SkASSERT(rec.fClip->isRect()); const SkIRect& r = rec.fClip->getBounds(); - + for (int i = 0; i < count; i++) { int x = SkScalarFloor(devPts[i].fX); int y = SkScalarFloor(devPts[i].fY); @@ -352,7 +362,7 @@ static void bw_pt_rect_16_hair_proc(const PtProcRec& rec, uint32_t value; const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value); SkASSERT(bitmap); - + uint16_t* addr = bitmap->getAddr16(0, 0); int rb = bitmap->rowBytes(); @@ -415,13 +425,13 @@ static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[], for (int i = 0; i < count; i++) { SkFixed x = SkScalarToFixed(devPts[i].fX); SkFixed y = SkScalarToFixed(devPts[i].fY); - + SkXRect r; r.fLeft = x - radius; r.fTop = y - radius; r.fRight = x + radius; r.fBottom = y + radius; - + SkScan::FillXRect(r, rec.fClip, blitter); } } @@ -432,13 +442,13 @@ static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[], for (int i = 0; i < count; i++) { SkFixed x = SkScalarToFixed(devPts[i].fX); SkFixed y = SkScalarToFixed(devPts[i].fY); - + SkXRect r; r.fLeft = x - radius; r.fTop = y - radius; r.fRight = x + radius; r.fBottom = y + radius; - + SkScan::AntiFillXRect(r, rec.fClip, blitter); } } @@ -478,7 +488,7 @@ bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint, PtProcRec::Proc PtProcRec::chooseProc(SkBlitter* blitter) { Proc proc = NULL; - + // for our arrays SkASSERT(0 == SkCanvas::kPoints_PointMode); SkASSERT(1 == SkCanvas::kLines_PointMode); @@ -539,7 +549,8 @@ static bool bounder_points(SkBounder* bounder, SkCanvas::PointMode mode, #define MAX_DEV_PTS 32 void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, - const SkPoint pts[], const SkPaint& paint) const { + const SkPoint pts[], const SkPaint& paint, + bool forceUseDevice) const { // if we're in lines mode, force count to be even if (SkCanvas::kLines_PointMode == mode) { count &= ~(size_t)1; @@ -548,7 +559,7 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, if ((long)count <= 0) { return; } - + SkAutoRestoreBounder arb; if (fBounder) { @@ -563,7 +574,7 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, SkASSERT(pts != NULL); SkDEBUGCODE(this->validate();) - + // nothing to draw if (fClip->isEmpty() || (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { @@ -571,7 +582,7 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, } PtProcRec rec; - if (rec.init(mode, paint, fMatrix, fClip)) { + if (!forceUseDevice && rec.init(mode, paint, fMatrix, fClip)) { SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); SkPoint devPts[MAX_DEV_PTS]; @@ -580,7 +591,7 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, PtProcRec::Proc proc = rec.chooseProc(bltr); // we have to back up subsequent passes if we're in polygon mode const size_t backup = (SkCanvas::kPolygon_PointMode == mode); - + do { size_t n = count; if (n > MAX_DEV_PTS) { @@ -603,27 +614,36 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, SkScalar width = paint.getStrokeWidth(); SkScalar radius = SkScalarHalf(width); - + if (paint.getStrokeCap() == SkPaint::kRound_Cap) { SkPath path; SkMatrix preMatrix; - + path.addCircle(0, 0, radius); for (size_t i = 0; i < count; i++) { preMatrix.setTranslate(pts[i].fX, pts[i].fY); // pass true for the last point, since we can modify // then path then - this->drawPath(path, paint, &preMatrix, (count-1) == i); + if (fDevice) { + fDevice->drawPath(*this, path, paint, &preMatrix, + (count-1) == i); + } else { + this->drawPath(path, paint, &preMatrix, (count-1) == i); + } } } else { SkRect r; - + for (size_t i = 0; i < count; i++) { r.fLeft = pts[i].fX - radius; r.fTop = pts[i].fY - radius; r.fRight = r.fLeft + width; r.fBottom = r.fTop + width; - this->drawRect(r, paint); + if (fDevice) { + fDevice->drawRect(*this, r, paint); + } else { + this->drawRect(r, paint); + } } } break; @@ -638,7 +658,11 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, for (size_t i = 0; i < count; i += inc) { path.moveTo(pts[i]); path.lineTo(pts[i+1]); - this->drawPath(path, p, NULL, true); + if (fDevice) { + fDevice->drawPath(*this, path, p, NULL, true); + } else { + this->drawPath(path, p, NULL, true); + } path.rewind(); } break; @@ -760,7 +784,7 @@ public: fPaint->setColor(fColor); fPaint->setStrokeWidth(fWidth); } - + private: SkPaint* fPaint; SkColor fColor; @@ -816,7 +840,7 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint, if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style || paint.getRasterizer()) { SkPath* result = pathPtr; - + if (!pathIsMutable) { result = &tmpPath; pathIsMutable = true; @@ -833,7 +857,7 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint, } // at this point we're done with prePathMatrix SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) - + /* If the device thickness < 1.0, then make it a hairline, and modulate alpha if the thickness is even smaller (e.g. thickness == 0.5 @@ -841,27 +865,28 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint, */ SkAutoPaintRestoreColorStrokeWidth aprc(paint); - + // can we approximate a thin (but not hairline) stroke with an alpha-modulated // hairline? Only if the matrix scales evenly in X and Y, and the device-width is // less than a pixel - if (paint.getStyle() == SkPaint::kStroke_Style && paint.getXfermode() == NULL) { + if (paint.isAntiAlias() && + paint.getStyle() == SkPaint::kStroke_Style && paint.getXfermode() == NULL) { SkScalar width = paint.getStrokeWidth(); if (width > 0 && map_radius(*matrix, &width)) { int scale = (int)SkScalarMul(width, 256); int alpha = paint.getAlpha() * scale >> 8; - + // pretend to be a hairline, with a modulated alpha ((SkPaint*)&paint)->setAlpha(alpha); ((SkPaint*)&paint)->setStrokeWidth(0); } } - + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { doFill = paint.getFillPath(*pathPtr, &tmpPath); pathPtr = &tmpPath; } - + if (paint.getRasterizer()) { SkMask mask; if (paint.getRasterizer()->rasterize(*pathPtr, *matrix, @@ -933,7 +958,7 @@ void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const { SkASSERT(bitmap.getConfig() == SkBitmap::kA8_Config); - if (just_translate(*fMatrix, bitmap)) { + if (just_translate(*fMatrix, bitmap)) { int ix = SkScalarRound(fMatrix->getTranslateX()); int iy = SkScalarRound(fMatrix->getTranslateY()); @@ -942,17 +967,17 @@ void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, mask.fFormat = SkMask::kA8_Format; mask.fRowBytes = bitmap.rowBytes(); mask.fImage = bitmap.getAddr8(0, 0); - + this->drawDevMask(mask, paint); } else { // need to xform the bitmap first SkRect r; SkMask mask; - + r.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); fMatrix->mapRect(&r); r.round(&mask.fBounds); - + // set the mask's bounds to the transformed bitmap-bounds, // clipped to the actual device { @@ -976,14 +1001,14 @@ void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, SkAutoMalloc storage(size); mask.fImage = (uint8_t*)storage.get(); memset(mask.fImage, 0, size); - + // now draw our bitmap(src) into mask(dst), transformed by the matrix { SkBitmap device; device.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes); device.setPixels(mask.fImage); - + SkCanvas c(device); // need the unclipped top/left for the translate c.translate(-SkIntToScalar(mask.fBounds.fLeft), @@ -1008,9 +1033,9 @@ static bool clipped_out(const SkMatrix& m, const SkRegion& c, const SkRect& srcR) { SkRect dstR; SkIRect devIR; - + m.mapRect(&dstR, srcR); - dstR.roundOut(&devIR); + dstR.roundOut(&devIR); return c.quickReject(devIR); } @@ -1032,14 +1057,16 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { return; } - + +#ifndef SK_ALLOW_OVER_32K_BITMAPS // run away on too-big bitmaps for now (exceed 16.16) if (bitmap.width() > 32767 || bitmap.height() > 32767) { return; } - +#endif + SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style); - + SkMatrix matrix; if (!matrix.setConcat(*fMatrix, prematrix)) { return; @@ -1094,12 +1121,12 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, paint.getXfermode(), paint.getAlpha(), paint.getColorFilter()); #endif } - + // now make a temp draw on the stack, and use it // SkDraw draw(*this); draw.fMatrix = &matrix; - + if (bitmap.getConfig() == SkBitmap::kA8_Config) { draw.drawBitmapAsMask(bitmap, paint); } else { @@ -1108,7 +1135,7 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, SkRect r; r.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())); - // is this ok if paint has a rasterizer? + // is this ok if paint has a rasterizer? draw.drawRect(r, paint); } } @@ -1116,7 +1143,7 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) const { SkDEBUGCODE(this->validate();) - + // nothing to draw if (fClip->isEmpty() || bitmap.width() == 0 || bitmap.height() == 0 || @@ -1168,12 +1195,12 @@ void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, // tell the shader our offset matrix.setTranslate(r.fLeft, r.fTop); paint.getShader()->setLocalMatrix(matrix); - + SkDraw draw(*this); matrix.reset(); draw.fMatrix = &matrix; // call ourself with a rect - // is this OK if paint has a rasterizer? + // is this OK if paint has a rasterizer? draw.drawRect(r, paint); } @@ -1189,7 +1216,7 @@ static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc, const char* stop = text + byteLength; SkAutoKern autokern; - + while (text < stop) { // don't need x, y here, since all subpixel variants will have the // same advance @@ -1219,15 +1246,16 @@ void SkDraw::drawText_asPaths(const char text[], size_t byteLength, while ((iterPath = iter.next(&xpos)) != NULL) { matrix.postTranslate(xpos - prevXPos, 0); - this->drawPath(*iterPath, iter.getPaint(), &matrix, false); + const SkPaint& pnt = iter.getPaint(); + if (fDevice) { + fDevice->drawPath(*this, *iterPath, pnt, &matrix, false); + } else { + this->drawPath(*iterPath, pnt, &matrix, false); + } prevXPos = xpos; } } -#define kStdStrikeThru_Offset (-SK_Scalar1 * 6 / 21) -#define kStdUnderline_Offset (SK_Scalar1 / 9) -#define kStdUnderline_Thickness (SK_Scalar1 / 18) - static void draw_paint_rect(const SkDraw* draw, const SkPaint& paint, const SkRect& r, SkScalar textSize) { if (paint.getStyle() == SkPaint::kFill_Style) { @@ -1270,7 +1298,7 @@ static void handle_aftertext(const SkDraw* draw, const SkPaint& paint, } // disable warning : local variable used without having been initialized -#if defined _WIN32 && _MSC_VER >= 1300 +#if defined _WIN32 && _MSC_VER >= 1300 #pragma warning ( push ) #pragma warning ( disable : 4701 ) #endif @@ -1278,7 +1306,10 @@ static void handle_aftertext(const SkDraw* draw, const SkPaint& paint, ////////////////////////////////////////////////////////////////////////////// static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state, - const SkGlyph& glyph, int left, int top) { + SkFixed fx, SkFixed fy, + const SkGlyph& glyph) { + int left = SkFixedFloor(fx); + int top = SkFixedFloor(fy); SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); SkASSERT(state.fClip->isRect()); SkASSERT(NULL == state.fBounder); @@ -1303,8 +1334,8 @@ static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state, return; bounds = &storage; } - - uint8_t* aa = (uint8_t*)glyph.fImage; + + uint8_t* aa = (uint8_t*)glyph.fImage; if (NULL == aa) { aa = (uint8_t*)state.fCache->findImage(glyph); if (NULL == aa) { @@ -1319,7 +1350,10 @@ static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state, } static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state, - const SkGlyph& glyph, int left, int top) { + SkFixed fx, SkFixed fy, + const SkGlyph& glyph) { + int left = SkFixedFloor(fx); + int top = SkFixedFloor(fy); SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); SkASSERT(!state.fClip->isRect()); SkASSERT(NULL == state.fBounder); @@ -1341,7 +1375,7 @@ static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state, return; } } - + mask.fRowBytes = glyph.rowBytes(); mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); mask.fImage = (uint8_t*)aa; @@ -1353,7 +1387,10 @@ static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state, } static void D1G_Bounder(const SkDraw1Glyph& state, - const SkGlyph& glyph, int left, int top) { + SkFixed fx, SkFixed fy, + const SkGlyph& glyph) { + int left = SkFixedFloor(fx); + int top = SkFixedFloor(fy); SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); SkMask mask; @@ -1473,14 +1510,25 @@ void SkDraw::drawText(const char text[], size_t byteLength, SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); - SkAutoGlyphCache autoCache(paint, fMatrix); + const SkMatrix* matrix = fMatrix; + SkFixed finalFYMask = ~0xFFFF; // trunc fy; + if (fProcs && fProcs->fD1GProc) { + // only support the fMVMatrix (for now) for the GPU case, which also + // sets the fD1GProc + if (fMVMatrix) { + matrix = fMVMatrix; + finalFYMask = ~0; // don't truncate + } + } + + SkAutoGlyphCache autoCache(paint, matrix); SkGlyphCache* cache = autoCache.getCache(); - SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); - + SkAutoBlitterChoose blitter(*fBitmap, *matrix, paint); + // transform our starting point { SkPoint loc; - fMatrix->mapXY(x, y, &loc); + matrix->mapXY(x, y, &loc); x = loc.fX; y = loc.fY; } @@ -1501,35 +1549,38 @@ void SkDraw::drawText(const char text[], size_t byteLength, x -= stopX; y -= stopY; } - + SkFixed fx = SkScalarToFixed(x); SkFixed fy = SkScalarToFixed(y); const char* stop = text + byteLength; + SkFixed fxMask = ~0; + SkFixed fyMask = ~0; if (paint.isSubpixelText()) { - RoundBaseline roundBaseline = computeRoundBaseline(*fMatrix); + RoundBaseline roundBaseline = computeRoundBaseline(*matrix); if (kRound_Y_Baseline == roundBaseline) { - fy = (fy + 0x8000) & ~0xFFFF; + fyMask = 0; +// fy = (fy + 0x8000) & ~0xFFFF; } else if (kRound_X_Baseline == roundBaseline) { - fx = (fx + 0x8000) & ~0xFFFF; + fxMask = 0; } - } else { - // apply the bias here, so we don't have to add 1/2 in the loop - fx += SK_Fixed1/2; - fy += SK_Fixed1/2; } + // apply the bias here, so we don't have to add 1/2 in the loop + fx += SK_FixedHalf; + fy += SK_FixedHalf; + fy &= finalFYMask; SkAutoKern autokern; SkDraw1Glyph d1g; SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache); while (text < stop) { - const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy); + const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask); fx += autokern.adjust(glyph); if (glyph.fWidth) { - proc(d1g, glyph, SkFixedFloor(fx), SkFixedFloor(fy)); + proc(d1g, fx, fy, glyph); } fx += glyph.fAdvanceX; fy += glyph.fAdvanceY; @@ -1567,7 +1618,7 @@ static AlignProc pick_align_proc(SkPaint::Align align) { static const AlignProc gProcs[] = { leftAlignProc, centerAlignProc, rightAlignProc }; - + SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs)); return gProcs[align]; @@ -1576,14 +1627,14 @@ static AlignProc pick_align_proc(SkPaint::Align align) { class TextMapState { public: mutable SkPoint fLoc; - + TextMapState(const SkMatrix& matrix, SkScalar y) : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {} typedef void (*Proc)(const TextMapState&, const SkScalar pos[]); - + Proc pickProc(int scalarsPerPosition); - + private: const SkMatrix& fMatrix; SkMatrix::MapXYProc fProc; @@ -1594,17 +1645,17 @@ private: static void MapXProc(const TextMapState& state, const SkScalar pos[]) { state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc); } - + static void MapXYProc(const TextMapState& state, const SkScalar pos[]) { state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc); } - + static void MapOnlyScaleXProc(const TextMapState& state, const SkScalar pos[]) { state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX, state.fTransformedY); } - + static void MapOnlyTransXProc(const TextMapState& state, const SkScalar pos[]) { state.fLoc.set(*pos + state.fTransX, state.fTransformedY); @@ -1613,7 +1664,7 @@ private: TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) { SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); - + if (1 == scalarsPerPosition) { unsigned mtype = fMatrix.getType(); if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) { @@ -1655,53 +1706,68 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, return; } + const SkMatrix* matrix = fMatrix; + if (fProcs && fProcs->fD1GProc) { + // only support the fMVMatrix (for now) for the GPU case, which also + // sets the fD1GProc + if (fMVMatrix) { + matrix = fMVMatrix; + } + } + SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); - SkAutoGlyphCache autoCache(paint, fMatrix); + SkAutoGlyphCache autoCache(paint, matrix); SkGlyphCache* cache = autoCache.getCache(); - SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint); - + SkAutoBlitterChoose blitter(*fBitmap, *matrix, paint); + const char* stop = text + byteLength; AlignProc alignProc = pick_align_proc(paint.getTextAlign()); SkDraw1Glyph d1g; SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache); - TextMapState tms(*fMatrix, constY); + TextMapState tms(*matrix, constY); TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition); if (paint.isSubpixelText()) { // maybe we should skip the rounding if linearText is set - RoundBaseline roundBaseline = computeRoundBaseline(*fMatrix); + RoundBaseline roundBaseline = computeRoundBaseline(*matrix); if (SkPaint::kLeft_Align == paint.getTextAlign()) { while (text < stop) { + tmsProc(tms, pos); - + SkFixed fx = SkScalarToFixed(tms.fLoc.fX); SkFixed fy = SkScalarToFixed(tms.fLoc.fY); + SkFixed fxMask = ~0; + SkFixed fyMask = ~0; if (kRound_Y_Baseline == roundBaseline) { - fy = (fy + 0x8000) & ~0xFFFF; + fyMask = 0; } else if (kRound_X_Baseline == roundBaseline) { - fx = (fx + 0x8000) & ~0xFFFF; + fxMask = 0; } - - const SkGlyph& glyph = glyphCacheProc(cache, &text, fx, fy); - + + const SkGlyph& glyph = glyphCacheProc(cache, &text, + fx & fxMask, fy & fyMask); + if (glyph.fWidth) { - proc(d1g, glyph, SkFixedFloor(fx), SkFixedFloor(fy)); + proc(d1g, fx, fy, glyph); } pos += scalarsPerPosition; } } else { while (text < stop) { const SkGlyph* glyph = &glyphCacheProc(cache, &text, 0, 0); - + if (glyph->fWidth) { SkDEBUGCODE(SkFixed prevAdvX = glyph->fAdvanceX;) SkDEBUGCODE(SkFixed prevAdvY = glyph->fAdvanceY;) SkFixed fx, fy; + SkFixed fxMask = ~0; + SkFixed fyMask = ~0; tmsProc(tms, pos); - + { SkIPoint fixedLoc; alignProc(tms.fLoc, *glyph, &fixedLoc); @@ -1709,19 +1775,19 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, fy = fixedLoc.fY; if (kRound_Y_Baseline == roundBaseline) { - fy = (fy + 0x8000) & ~0xFFFF; + fyMask = 0; } else if (kRound_X_Baseline == roundBaseline) { - fx = (fx + 0x8000) & ~0xFFFF; + fxMask = 0; } } - + // have to call again, now that we've been "aligned" - glyph = &glyphCacheProc(cache, &text, fx, fy); + glyph = &glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask); // the assumption is that the advance hasn't changed SkASSERT(prevAdvX == glyph->fAdvanceX); SkASSERT(prevAdvY == glyph->fAdvanceY); - - proc(d1g, *glyph, SkFixedFloor(fx), SkFixedFloor(fy)); + + proc(d1g, fx, fy, *glyph); } pos += scalarsPerPosition; } @@ -1730,15 +1796,15 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, while (text < stop) { // the last 2 parameters are ignored const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); - + if (glyph.fWidth) { tmsProc(tms, pos); - + SkIPoint fixedLoc; alignProc(tms.fLoc, glyph, &fixedLoc); - - proc(d1g, glyph, - SkFixedRound(fixedLoc.fX), SkFixedRound(fixedLoc.fY)); + + proc(d1g, fixedLoc.fX + SK_FixedHalf, + fixedLoc.fY + SK_FixedHalf, glyph); } pos += scalarsPerPosition; } @@ -1856,7 +1922,7 @@ void SkDraw::drawTextOnPath(const char text[], size_t byteLength, SkScalar scale = iter.getPathScale(); scaledMatrix.setScale(scale, scale); - + while ((iterPath = iter.next(&xpos)) != NULL) { SkPath tmp; SkMatrix m(scaledMatrix); @@ -1866,7 +1932,11 @@ void SkDraw::drawTextOnPath(const char text[], size_t byteLength, m.postConcat(*matrix); } morphpath(&tmp, *iterPath, meas, m); - this->drawPath(tmp, iter.getPaint()); + if (fDevice) { + fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true); + } else { + this->drawPath(tmp, iter.getPaint(), NULL, true); + } } } @@ -1946,15 +2016,15 @@ struct VertState { fCount = vCount; } } - - typedef bool (*Proc)(VertState*); + + typedef bool (*Proc)(VertState*); Proc chooseProc(SkCanvas::VertexMode mode); private: int fCount; int fCurrIndex; const uint16_t* fIndices; - + static bool Triangles(VertState*); static bool TrianglesX(VertState*); static bool TriangleStrip(VertState*); @@ -2071,7 +2141,7 @@ static HairProc ChooseHairProc(bool doAntiAlias) { static bool texture_to_matrix(const VertState& state, const SkPoint verts[], const SkPoint texs[], SkMatrix* matrix) { SkPoint src[3], dst[3]; - + src[0] = texs[state.f0]; src[1] = texs[state.f1]; src[2] = texs[state.f2]; @@ -2086,18 +2156,18 @@ public: SkTriColorShader() {} bool setup(const SkPoint pts[], const SkColor colors[], int, int, int); - + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); - + protected: SkTriColorShader(SkFlattenableReadBuffer& buffer) : SkShader(buffer) {} - + virtual Factory getFactory() { return CreateProc; } - + private: SkMatrix fDstToUnit; SkPMColor fColors[3]; - + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return SkNEW_ARGS(SkTriColorShader, (buffer)); } @@ -2106,11 +2176,11 @@ private: bool SkTriColorShader::setup(const SkPoint pts[], const SkColor colors[], int index0, int index1, int index2) { - + fColors[0] = SkPreMultiplyColor(colors[index0]); fColors[1] = SkPreMultiplyColor(colors[index1]); fColors[2] = SkPreMultiplyColor(colors[index2]); - + SkMatrix m, im; m.reset(); m.set(0, pts[index1].fX - pts[index0].fX); @@ -2141,11 +2211,11 @@ static int ScalarTo256(SkScalar v) { void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) { SkPoint src; - + for (int i = 0; i < count; i++) { fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src); x += 1; - + int scale1 = ScalarTo256(src.fX); int scale2 = ScalarTo256(src.fY); int scale0 = 256 - scale1 - scale2; @@ -2157,7 +2227,7 @@ void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) { } scale0 = 0; } - + dstC[i] = SkAlphaMulQ(fColors[0], scale0) + SkAlphaMulQ(fColors[1], scale1) + SkAlphaMulQ(fColors[2], scale2); @@ -2170,18 +2240,18 @@ void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count, const uint16_t indices[], int indexCount, const SkPaint& paint) const { SkASSERT(0 == count || NULL != vertices); - + // abort early if there is nothing to draw if (count < 3 || (indices && indexCount < 3) || fClip->isEmpty() || (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { return; } - + // transform out vertices into device coordinates SkAutoSTMalloc<16, SkPoint> storage(count); SkPoint* devVerts = storage.get(); fMatrix->mapPoints(devVerts, vertices, count); - + if (fBounder) { SkRect bounds; bounds.set(devVerts, count); @@ -2189,7 +2259,7 @@ void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count, return; } } - + /* We can draw the vertices in 1 of 4 ways: @@ -2197,7 +2267,7 @@ void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count, - just colors (no shader/texture[], has colors[]) - just texture (has shader/texture[], no colors[]) - colors * texture (has shader/texture[], has colors[]) - + Thus for texture drawing, we need both texture[] and a shader. */ @@ -2240,17 +2310,17 @@ void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count, // setup our state and function pointer for iterating triangles VertState state(count, indices, indexCount); VertState::Proc vertProc = state.chooseProc(vmode); - + if (NULL != textures || NULL != colors) { SkMatrix localM, tempM; bool hasLocalM = shader && shader->getLocalMatrix(&localM); - + if (NULL != colors) { if (!triShader.setContext(*fBitmap, p, *fMatrix)) { colors = NULL; } } - + while (vertProc(&state)) { if (NULL != textures) { if (texture_to_matrix(state, vertices, textures, &tempM)) { @@ -2307,6 +2377,9 @@ void SkDraw::validate() const { br.set(0, 0, fBitmap->width(), fBitmap->height()); SkASSERT(cr.isEmpty() || br.contains(cr)); + + // assert that both are null, or both are not-null + SkASSERT(!fMVMatrix == !fExtMatrix); } #endif @@ -2420,12 +2493,12 @@ static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds, pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf); pathBounds.roundOut(bounds); } - + if (filter) { SkASSERT(filterMatrix); - + SkMask srcM, dstM; - + srcM.fBounds = *bounds; srcM.fFormat = SkMask::kA8_Format; srcM.fImage = NULL; @@ -2438,7 +2511,7 @@ static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds, if (clipBounds && !SkIRect::Intersects(*clipBounds, *bounds)) { return false; } - + // (possibly) trim the srcM bounds to reflect the clip // (plus whatever slop the filter needs) if (clipBounds && !clipBounds->contains(*bounds)) { @@ -2488,7 +2561,7 @@ bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds, if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds)) return false; } - + if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) { mask->fFormat = SkMask::kA8_Format; mask->fRowBytes = mask->fBounds.width(); @@ -2504,6 +2577,6 @@ bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds, if (SkMask::kJustComputeBounds_CreateMode != mode) { draw_into_mask(*mask, devPath); } - + return true; } diff --git a/src/core/SkDrawProcs.h b/src/core/SkDrawProcs.h index d64c088..7d69465 100644 --- a/src/core/SkDrawProcs.h +++ b/src/core/SkDrawProcs.h @@ -13,7 +13,9 @@ struct SkDraw1Glyph { SkGlyphCache* fCache; SkIRect fClipBounds; - typedef void (*Proc)(const SkDraw1Glyph&, const SkGlyph&, int x, int y); + // The fixed x,y have been pre-rounded (i.e. 1/2 has already been added), + // so the impls need just trunc down to an int + typedef void (*Proc)(const SkDraw1Glyph&, SkFixed x, SkFixed y, const SkGlyph&); Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache); }; diff --git a/src/core/SkDrawing.cpp b/src/core/SkDrawing.cpp new file mode 100644 index 0000000..f54afcc --- /dev/null +++ b/src/core/SkDrawing.cpp @@ -0,0 +1,152 @@ +#include "SkDrawing.h" +#include "SkCanvas.h" + +SkDrawing::SkDrawing() { + fMatrix.reset(); + fParent = fFirstChild = fNextSibling = fPrevSibling = NULL; +} + +SkDrawing::~SkDrawing() { + this->detachAllChildren(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkDrawing::resetMatrix() { + fMatrix.reset(); +} + +void SkDrawing::getMatrix(SkMatrix* matrix) const { + if (matrix) { + *matrix = fMatrix; + } +} + +void SkDrawing::setMatrix(const SkMatrix& matrix) { + if (fMatrix != matrix) { + this->inval(); + fMatrix = matrix; + this->inval(); + } +} + +void SkDrawing::draw(SkCanvas* canvas) { + SkAutoCanvasRestore ar(canvas, false); + canvas->save(SkCanvas::kMatrix_SaveFlag); + canvas->concat(fMatrix); + + this->onDraw(canvas); + + B2FIter iter(this); + SkDrawing* child; + while ((child = iter.next()) != NULL) { + child->draw(canvas); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkDrawing::detachFromParent() { + SkDrawing* parent = fParent; + + if (NULL == parent) { + return; + } + + this->inval(); + + SkDrawing* 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(); +} + +SkDrawing* SkDrawing::attachChildToBack(SkDrawing* child) { + SkASSERT(child != this); + + if (child == NULL || fFirstChild == child) { + return child; + } + + child->ref(); + child->detachFromParent(); + + 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(); + return child; +} + +SkDrawing* SkDrawing::attachChildToFront(SkDrawing* child) { + SkASSERT(child != this); + + if (child == NULL || fFirstChild && fFirstChild->fPrevSibling == child) { + return child; + } + + child->ref(); + child->detachFromParent(); + + 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(); + return child; +} + +void SkDrawing::detachAllChildren() { + while (fFirstChild) { + fFirstChild->detachFromParent(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkDrawing::B2FIter::B2FIter(const SkDrawing* parent) { + fFirstChild = parent ? parent->fFirstChild : NULL; + fChild = fFirstChild; +} + +SkDrawing* SkDrawing::B2FIter::next() { + SkDrawing* curr = fChild; + + if (fChild) { + SkDrawing* next = fChild->fNextSibling; + if (next == fFirstChild) { + next = NULL; + } + fChild = next; + } + return curr; +} + + diff --git a/src/core/SkFlate.cpp b/src/core/SkFlate.cpp new file mode 100644 index 0000000..ade1658 --- /dev/null +++ b/src/core/SkFlate.cpp @@ -0,0 +1,129 @@ +/* + * 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 "SkFlate.h" +#include "SkStream.h" + +#ifndef SK_ZLIB_INCLUDE +bool SkFlate::HaveFlate() { return false; } +bool SkFlate::Deflate(SkStream*, SkDynamicMemoryWStream*) { return false; } +bool SkFlate::Inflate(SkStream*, SkDynamicMemoryWStream*) { return false; } +#else + +// static +const size_t SkFlate::kBufferSize = 1024; + +// static +bool SkFlate::HaveFlate() { + return true; +} + +namespace { + +#include SK_ZLIB_INCLUDE + +bool doFlate(bool compress, const size_t kBufferSize, SkStream* src, + SkDynamicMemoryWStream* dst) { + uint8_t inputBuffer[kBufferSize]; + uint8_t outputBuffer[kBufferSize]; + z_stream flateData; + flateData.zalloc = NULL; + flateData.zfree = NULL; + flateData.next_in = NULL; + flateData.avail_in = 0; + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + int rc; + if (compress) + rc = deflateInit(&flateData, Z_DEFAULT_COMPRESSION); + else + rc = inflateInit(&flateData); + if (rc != Z_OK) + return false; + + uint8_t* input = (uint8_t*)src->getMemoryBase(); + size_t inputLength = src->getLength(); + if (input == NULL || inputLength == 0) { + input = NULL; + flateData.next_in = inputBuffer; + flateData.avail_in = 0; + } else { + flateData.next_in = input; + flateData.avail_in = inputLength; + } + + rc = Z_OK; + while (true) { + if (flateData.avail_out < kBufferSize) { + if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) { + rc = Z_BUF_ERROR; + break; + } + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + } + if (rc != Z_OK) + break; + if (flateData.avail_in == 0) { + if (input != NULL) + break; + size_t read = src->read(&inputBuffer, kBufferSize); + if (read == 0) + break; + flateData.next_in = inputBuffer; + flateData.avail_in = read; + } + if (compress) + rc = deflate(&flateData, Z_NO_FLUSH); + else + rc = inflate(&flateData, Z_NO_FLUSH); + } + while (rc == Z_OK) { + if (compress) + rc = deflate(&flateData, Z_FINISH); + else + rc = inflate(&flateData, Z_FINISH); + if (flateData.avail_out < kBufferSize) { + if (!dst->write(outputBuffer, kBufferSize - flateData.avail_out)) + return false; + flateData.next_out = outputBuffer; + flateData.avail_out = kBufferSize; + } + } + + if (compress) + deflateEnd(&flateData); + else + inflateEnd(&flateData); + if (rc == Z_STREAM_END) + return true; + return false; +} + +} + +// static +bool SkFlate::Deflate(SkStream* src, SkDynamicMemoryWStream* dst) { + return doFlate(true, kBufferSize, src, dst); +} + +// static +bool SkFlate::Inflate(SkStream* src, SkDynamicMemoryWStream* dst) { + return doFlate(false, kBufferSize, src, dst); +} + +#endif + diff --git a/src/core/SkFlattenable.cpp b/src/core/SkFlattenable.cpp index 3558519..dcdaa92 100644 --- a/src/core/SkFlattenable.cpp +++ b/src/core/SkFlattenable.cpp @@ -15,10 +15,10 @@ void SkFlattenable::flatten(SkFlattenableWriteBuffer&) SkFlattenableReadBuffer::SkFlattenableReadBuffer() { fRCArray = NULL; fRCCount = 0; - + fTFArray = NULL; fTFCount = 0; - + fFactoryArray = NULL; fFactoryCount = 0; } @@ -27,10 +27,10 @@ SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data) : INHERITED(data, 1024 * 1024) { fRCArray = NULL; fRCCount = 0; - + fTFArray = NULL; fTFCount = 0; - + fFactoryArray = NULL; fFactoryCount = 0; } @@ -39,10 +39,10 @@ SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data, size_t size) : INHERITED(data, size) { fRCArray = NULL; fRCCount = 0; - + fTFArray = NULL; fTFCount = 0; - + fFactoryArray = NULL; fFactoryCount = 0; } @@ -72,7 +72,7 @@ SkRefCnt* SkFlattenableReadBuffer::readRefCnt() { SkFlattenable* SkFlattenableReadBuffer::readFlattenable() { SkFlattenable::Factory factory = NULL; - + if (fFactoryCount > 0) { uint32_t index = this->readU32(); if (index > 0) { @@ -123,9 +123,9 @@ SkFlattenableWriteBuffer::SkFlattenableWriteBuffer(size_t minSize) : } SkFlattenableWriteBuffer::~SkFlattenableWriteBuffer() { - fRCRecorder->safeUnref(); - fTFRecorder->safeUnref(); - fFactoryRecorder->safeUnref(); + SkSafeUnref(fRCRecorder); + SkSafeUnref(fTFRecorder); + SkSafeUnref(fFactoryRecorder); } SkRefCntRecorder* SkFlattenableWriteBuffer::setRefCntRecorder( @@ -173,7 +173,7 @@ void SkFlattenableWriteBuffer::writeFlattenable(SkFlattenable* flattenable) { } else { this->writeFunctionPtr((void*)factory); } - + if (factory) { // make room for the size of the flatttened object (void)this->reserve(sizeof(uint32_t)); @@ -223,15 +223,15 @@ static Pair gPairs[MAX_PAIR_COUNT]; void SkFlattenable::Register(const char name[], Factory factory) { SkASSERT(name); SkASSERT(factory); - + static bool gOnce; if (!gOnce) { gCount = 0; gOnce = true; } - + SkASSERT(gCount < MAX_PAIR_COUNT); - + gPairs[gCount].fName = name; gPairs[gCount].fFactory = factory; gCount += 1; diff --git a/src/core/SkFloat.h b/src/core/SkFloat.h index 31aaeed..9435dd0 100644 --- a/src/core/SkFloat.h +++ b/src/core/SkFloat.h @@ -96,8 +96,6 @@ public: private: int32_t fPacked; - SkFloat(int32_t packed) : fPacked(fPacked) {} - public: static int GetShift(int32_t packed, int shift); static int32_t SetShift(int value, int shift); diff --git a/src/core/SkFontHost.cpp b/src/core/SkFontHost.cpp new file mode 100644 index 0000000..435b5a5 --- /dev/null +++ b/src/core/SkFontHost.cpp @@ -0,0 +1,45 @@ +/* libs/graphics/sgl/SkFontHost.cpp +** +** Copyright 2009, 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 "SkFontHost.h" + +static SkFontHost::LCDOrientation gLCDOrientation = SkFontHost::kHorizontal_LCDOrientation; +static SkFontHost::LCDOrder gLCDOrder = SkFontHost::kRGB_LCDOrder; + +// static +SkFontHost::LCDOrientation SkFontHost::GetSubpixelOrientation() +{ + return gLCDOrientation; +} + +// static +void SkFontHost::SetSubpixelOrientation(LCDOrientation orientation) +{ + gLCDOrientation = orientation; +} + +// static +SkFontHost::LCDOrder SkFontHost::GetSubpixelOrder() +{ + return gLCDOrder; +} + +// static +void SkFontHost::SetSubpixelOrder(LCDOrder order) +{ + gLCDOrder = order; +} diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp index 34a0fe0..3c9e9f9 100644 --- a/src/core/SkGeometry.cpp +++ b/src/core/SkGeometry.cpp @@ -19,12 +19,19 @@ #include "Sk64.h" #include "SkMatrix.h" -bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2]) { +bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2], bool* ambiguous) { + if (ambiguous) { + *ambiguous = false; + } // Determine quick discards. // Consider query line going exactly through point 0 to not // intersect, for symmetry with SkXRayCrossesMonotonicCubic. - if (pt.fY == pts[0].fY) + if (pt.fY == pts[0].fY) { + if (ambiguous) { + *ambiguous = true; + } return false; + } if (pt.fY < pts[0].fY && pt.fY < pts[1].fY) return false; if (pt.fY > pts[0].fY && pt.fY > pts[1].fY) @@ -34,10 +41,27 @@ bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2]) { // Determine degenerate cases if (SkScalarNearlyZero(pts[0].fY - pts[1].fY)) return false; - if (SkScalarNearlyZero(pts[0].fX - pts[1].fX)) + if (SkScalarNearlyZero(pts[0].fX - pts[1].fX)) { // We've already determined the query point lies within the // vertical range of the line segment. - return pt.fX <= pts[0].fX; + if (pt.fX <= pts[0].fX) { + if (ambiguous) { + *ambiguous = (pt.fY == pts[1].fY); + } + return true; + } + return false; + } + // Ambiguity check + if (pt.fY == pts[1].fY) { + if (pt.fX <= pts[1].fX) { + if (ambiguous) { + *ambiguous = true; + } + return true; + } + return false; + } // Full line segment evaluation SkScalar delta_y = pts[1].fY - pts[0].fY; SkScalar delta_x = pts[1].fX - pts[0].fX; @@ -986,7 +1010,11 @@ int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tV return count + 1; } -bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) { +bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous) { + if (ambiguous) { + *ambiguous = false; + } + // Find the minimum and maximum y of the extrema, which are the // first and last points since this cubic is monotonic SkScalar min_y = SkMinScalar(cubic[0].fY, cubic[3].fY); @@ -996,9 +1024,14 @@ bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) { || pt.fY < min_y || pt.fY > max_y) { // The query line definitely does not cross the curve + if (ambiguous) { + *ambiguous = (pt.fY == cubic[0].fY); + } return false; } + bool pt_at_extremum = (pt.fY == cubic[3].fY); + SkScalar min_x = SkMinScalar( SkMinScalar( @@ -1007,6 +1040,9 @@ bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) { cubic[3].fX); if (pt.fX < min_x) { // The query line definitely crosses the curve + if (ambiguous) { + *ambiguous = pt_at_extremum; + } return true; } @@ -1053,23 +1089,39 @@ bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) { } while (++iter < kMaxIter && !SkScalarNearlyZero(eval.fY - pt.fY)); if (pt.fX <= eval.fX) { + if (ambiguous) { + *ambiguous = pt_at_extremum; + } return true; } return false; } -int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4]) { +int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous) { int num_crossings = 0; SkPoint monotonic_cubics[10]; int num_monotonic_cubics = SkChopCubicAtYExtrema(cubic, monotonic_cubics); - if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[0])) + if (ambiguous) { + *ambiguous = false; + } + bool locally_ambiguous; + if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[0], &locally_ambiguous)) ++num_crossings; + if (ambiguous) { + *ambiguous |= locally_ambiguous; + } if (num_monotonic_cubics > 0) - if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[3])) + if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[3], &locally_ambiguous)) ++num_crossings; + if (ambiguous) { + *ambiguous |= locally_ambiguous; + } if (num_monotonic_cubics > 1) - if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[6])) + if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[6], &locally_ambiguous)) ++num_crossings; + if (ambiguous) { + *ambiguous |= locally_ambiguous; + } return num_crossings; } diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp index cfd355c..199ac14 100644 --- a/src/core/SkGlyphCache.cpp +++ b/src/core/SkGlyphCache.cpp @@ -96,22 +96,7 @@ SkGlyphCache::~SkGlyphCache() { /////////////////////////////////////////////////////////////////////////////// #ifdef SK_DEBUG -class AutoCheckForNull { -public: - AutoCheckForNull(const SkTDArray<SkGlyph*>& array) : fArray(array) { - for (int i = 0; i < array.count(); i++) - SkASSERT(array[i]); - } - ~AutoCheckForNull() { - const SkTDArray<SkGlyph*>& array = fArray; - for (int i = 0; i < array.count(); i++) { - SkASSERT(array[i]); - } - } -private: - const SkTDArray<SkGlyph*>& fArray; -}; -#define VALIDATE() AutoCheckForNull acfn(fGlyphArray) +#define VALIDATE() AutoValidate av(this) #else #define VALIDATE() #endif @@ -288,9 +273,7 @@ SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) { glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph), SkChunkAlloc::kThrow_AllocFailType); - glyph->fID = id; - glyph->fImage = NULL; - glyph->fPath = NULL; + glyph->init(id); *fGlyphArray.insert(hi) = glyph; if (kJustAdvance_MetricsType == mtype) { @@ -306,7 +289,7 @@ SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) { } const void* SkGlyphCache::findImage(const SkGlyph& glyph) { - if (glyph.fWidth) { + if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) { if (glyph.fImage == NULL) { size_t size = glyph.computeImageSize(); const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size, @@ -519,6 +502,9 @@ SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc, cache = SkNEW_ARGS(SkGlyphCache, (desc)); FOUND_IT: + + AutoValidate av(cache); + if (proc(cache, context)) { // stay detached if (insideMutex) { SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed); @@ -549,6 +535,7 @@ void SkGlyphCache::AttachCache(SkGlyphCache* cache) { SkAutoMutexAcquire ac(globals.fMutex); globals.validate(); + cache->validate(); // if we have a fixed budget for our cache, do a purge here { @@ -664,3 +651,19 @@ size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals, return bytesFreed; } +/////////////////////////////////////////////////////////////////////////////// +#ifdef SK_DEBUG + +void SkGlyphCache::validate() const { + int count = fGlyphArray.count(); + for (int i = 0; i < count; i++) { + const SkGlyph* glyph = fGlyphArray[i]; + SkASSERT(glyph); + SkASSERT(fGlyphAlloc.contains(glyph)); + if (glyph->fImage) { + SkASSERT(fImageAlloc.contains(glyph->fImage)); + } + } +} + +#endif diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h index cc81ed6..16330f8 100644 --- a/src/core/SkGlyphCache.h +++ b/src/core/SkGlyphCache.h @@ -91,7 +91,9 @@ public: const SkPaint::FontMetrics& getFontMetricsY() const { return fFontMetricsY; } - + + const SkDescriptor& getDescriptor() const { return *fDesc; } + /* AuxProc/Data allow a client to associate data with this cache entry. Multiple clients can use this, as their data is keyed with a function pointer. In addition to serving as a key, the function pointer is called @@ -154,6 +156,31 @@ public: */ static bool SetCacheUsed(size_t bytesUsed); +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + + class AutoValidate : SkNoncopyable { + public: + AutoValidate(const SkGlyphCache* cache) : fCache(cache) { + if (fCache) { + fCache->validate(); + } + } + ~AutoValidate() { + if (fCache) { + fCache->validate(); + } + } + void forget() { + fCache = NULL; + } + private: + const SkGlyphCache* fCache; + }; + private: SkGlyphCache(const SkDescriptor*); ~SkGlyphCache(); diff --git a/src/core/SkMallocPixelRef.cpp b/src/core/SkMallocPixelRef.cpp new file mode 100644 index 0000000..7b97f16 --- /dev/null +++ b/src/core/SkMallocPixelRef.cpp @@ -0,0 +1,57 @@ +#include "SkMallocPixelRef.h" +#include "SkBitmap.h" +#include "SkFlattenable.h" + +SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size, + SkColorTable* ctable) { + if (NULL == storage) { + storage = sk_malloc_throw(size); + } + fStorage = storage; + fSize = size; + fCTable = ctable; + SkSafeRef(ctable); +} + +SkMallocPixelRef::~SkMallocPixelRef() { + SkSafeUnref(fCTable); + sk_free(fStorage); +} + +void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) { + *ct = fCTable; + return fStorage; +} + +void SkMallocPixelRef::onUnlockPixels() { + // nothing to do +} + +void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + + buffer.write32(fSize); + buffer.writePad(fStorage, fSize); + if (fCTable) { + buffer.writeBool(true); + fCTable->flatten(buffer); + } else { + buffer.writeBool(false); + } +} + +SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer, NULL) { + fSize = buffer.readU32(); + fStorage = sk_malloc_throw(fSize); + buffer.read(fStorage, fSize); + if (buffer.readBool()) { + fCTable = SkNEW_ARGS(SkColorTable, (buffer)); + } else { + fCTable = NULL; + } +} + +static SkPixelRef::Registrar reg("SkMallocPixelRef", + SkMallocPixelRef::Create); + diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp index f7d6ca0..f2d1708 100644 --- a/src/core/SkMatrix.cpp +++ b/src/core/SkMatrix.cpp @@ -653,9 +653,10 @@ bool SkMatrix::postConcat(const SkMatrix& mat) { det = (double)mat[SkMatrix::kMScaleX] * mat[SkMatrix::kMScaleY] - (double)mat[SkMatrix::kMSkewX] * mat[SkMatrix::kMSkewY]; } - // Since the determinant is on the order of the square of the matrix members, - // compare to the square of the default nearly-zero constant - if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) { + // Since the determinant is on the order of the cube of the matrix members, + // compare to the cube of the default nearly-zero constant (although an + // estimate of the condition number would be better if it wasn't so expensive). + if (SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero * SK_ScalarNearlyZero)) { return 0; } return 1.0 / det; @@ -716,6 +717,24 @@ bool SkMatrix::postConcat(const SkMatrix& mat) { } #endif +bool SkMatrix::pdfTransform(SkScalar transform[6]) const { + SkMatrix identity; + const SkMatrix* use = this; + bool ret = true; + if (has_perspective(*this)) { + identity.reset(); + use = &identity; + ret = false; + } + transform[0] = use->fMat[kMScaleX]; + transform[1] = use->fMat[kMSkewY]; + transform[2] = use->fMat[kMSkewX]; + transform[3] = use->fMat[kMScaleY]; + transform[4] = use->fMat[kMTransX]; + transform[5] = use->fMat[kMTransY]; + return true; +} + bool SkMatrix::invert(SkMatrix* inv) const { int isPersp = has_perspective(*this); int shift; @@ -1576,8 +1595,10 @@ uint32_t SkMatrix::flatten(void* buffer) const { } uint32_t SkMatrix::unflatten(const void* buffer) { - memcpy(fMat, buffer, 9 * sizeof(SkScalar)); - this->setTypeMask(kUnknown_Mask); + if (buffer) { + memcpy(fMat, buffer, 9 * sizeof(SkScalar)); + this->setTypeMask(kUnknown_Mask); + } return 9 * sizeof(SkScalar); } diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index f203d35..845896a 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -23,8 +23,10 @@ #include "SkPathEffect.h" #include "SkRasterizer.h" #include "SkShader.h" +#include "SkScalar.h" #include "SkScalerContext.h" #include "SkStroke.h" +#include "SkTextFormatParams.h" #include "SkTypeface.h" #include "SkXfermode.h" #include "SkAutoKern.h" @@ -66,53 +68,50 @@ SkPaint::SkPaint() { fGenerationID = 0; } -SkPaint::SkPaint(const SkPaint& src) -{ +SkPaint::SkPaint(const SkPaint& src) { memcpy(this, &src, sizeof(src)); - fTypeface->safeRef(); - fPathEffect->safeRef(); - fShader->safeRef(); - fXfermode->safeRef(); - fMaskFilter->safeRef(); - fColorFilter->safeRef(); - fRasterizer->safeRef(); - fLooper->safeRef(); + SkSafeRef(fTypeface); + SkSafeRef(fPathEffect); + SkSafeRef(fShader); + SkSafeRef(fXfermode); + SkSafeRef(fMaskFilter); + SkSafeRef(fColorFilter); + SkSafeRef(fRasterizer); + SkSafeRef(fLooper); } -SkPaint::~SkPaint() -{ - fTypeface->safeUnref(); - fPathEffect->safeUnref(); - fShader->safeUnref(); - fXfermode->safeUnref(); - fMaskFilter->safeUnref(); - fColorFilter->safeUnref(); - fRasterizer->safeUnref(); - fLooper->safeUnref(); +SkPaint::~SkPaint() { + SkSafeUnref(fTypeface); + SkSafeUnref(fPathEffect); + SkSafeUnref(fShader); + SkSafeUnref(fXfermode); + SkSafeUnref(fMaskFilter); + SkSafeUnref(fColorFilter); + SkSafeUnref(fRasterizer); + SkSafeUnref(fLooper); } -SkPaint& SkPaint::operator=(const SkPaint& src) -{ +SkPaint& SkPaint::operator=(const SkPaint& src) { SkASSERT(&src); - src.fTypeface->safeRef(); - src.fPathEffect->safeRef(); - src.fShader->safeRef(); - src.fXfermode->safeRef(); - src.fMaskFilter->safeRef(); - src.fColorFilter->safeRef(); - src.fRasterizer->safeRef(); - src.fLooper->safeRef(); - - fTypeface->safeUnref(); - fPathEffect->safeUnref(); - fShader->safeUnref(); - fXfermode->safeUnref(); - fMaskFilter->safeUnref(); - fColorFilter->safeUnref(); - fRasterizer->safeUnref(); - fLooper->safeUnref(); + SkSafeRef(src.fTypeface); + SkSafeRef(src.fPathEffect); + SkSafeRef(src.fShader); + SkSafeRef(src.fXfermode); + SkSafeRef(src.fMaskFilter); + SkSafeRef(src.fColorFilter); + SkSafeRef(src.fRasterizer); + SkSafeRef(src.fLooper); + + SkSafeUnref(fTypeface); + SkSafeUnref(fPathEffect); + SkSafeUnref(fShader); + SkSafeUnref(fXfermode); + SkSafeUnref(fMaskFilter); + SkSafeUnref(fColorFilter); + SkSafeUnref(fRasterizer); + SkSafeUnref(fLooper); uint32_t oldGenerationID = fGenerationID; memcpy(this, &src, sizeof(src)); @@ -121,13 +120,11 @@ SkPaint& SkPaint::operator=(const SkPaint& src) return *this; } -int operator==(const SkPaint& a, const SkPaint& b) -{ +int operator==(const SkPaint& a, const SkPaint& b) { return memcmp(&a, &b, sizeof(a)) == 0; } -void SkPaint::reset() -{ +void SkPaint::reset() { SkPaint init; uint32_t oldGenerationID = fGenerationID; @@ -187,6 +184,11 @@ void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) } } +void SkPaint::setAutohinted(bool useAutohinter) +{ + this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag)); +} + void SkPaint::setLinearText(bool doLinearText) { if (doLinearText != isLinearText()) { @@ -357,7 +359,7 @@ void SkPaint::setTextSize(SkScalar ts) } #ifdef SK_DEBUG else - SkDebugf("SkPaint::setTextSize() called with non-positive value\n"); + SkDebugf("SkPaint::setTextSize() called with negative value\n"); #endif } @@ -400,15 +402,13 @@ SkTypeface* SkPaint::setTypeface(SkTypeface* font) return font; } -SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) -{ +SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) { SkRefCnt_SafeAssign(fRasterizer, r); fGenerationID++; return r; } -SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) -{ +SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) { SkRefCnt_SafeAssign(fLooper, looper); fGenerationID++; return looper; @@ -419,8 +419,7 @@ SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) #include "SkGlyphCache.h" #include "SkUtils.h" -static void DetachDescProc(const SkDescriptor* desc, void* context) -{ +static void DetachDescProc(const SkDescriptor* desc, void* context) { *((SkGlyphCache**)context) = SkGlyphCache::DetachCache(desc); } @@ -450,7 +449,7 @@ int SkPaint::textToGlyphs(const void* textData, size_t byteLength, if (byteLength == 0) { return 0; } - + SkASSERT(textData != NULL); if (NULL == glyphs) { @@ -467,16 +466,16 @@ int SkPaint::textToGlyphs(const void* textData, size_t byteLength, } return 0; } - + // if we get here, we have a valid glyphs[] array, so time to fill it in - + // handle this encoding before the setup for the glyphcache if (this->getTextEncoding() == kGlyphID_TextEncoding) { // we want to ignore the low bit of byteLength memcpy(glyphs, textData, byteLength >> 1 << 1); return byteLength >> 1; } - + SkAutoGlyphCache autoCache(*this, NULL); SkGlyphCache* cache = autoCache.getCache(); @@ -508,9 +507,9 @@ bool SkPaint::containsText(const void* textData, size_t byteLength) const { if (0 == byteLength) { return true; } - + SkASSERT(textData != NULL); - + // handle this encoding before the setup for the glyphcache if (this->getTextEncoding() == kGlyphID_TextEncoding) { const uint16_t* glyphID = static_cast<const uint16_t*>(textData); @@ -522,7 +521,7 @@ bool SkPaint::containsText(const void* textData, size_t byteLength) const { } return true; } - + SkAutoGlyphCache autoCache(*this, NULL); SkGlyphCache* cache = autoCache.getCache(); @@ -573,43 +572,43 @@ void SkPaint::glyphsToUnichars(const uint16_t glyphs[], int count, /////////////////////////////////////////////////////////////////////////////// -static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getMetrics_utf8_next(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharMetrics(SkUTF8_NextUnichar(text)); } -static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getMetrics_utf8_prev(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharMetrics(SkUTF8_PrevUnichar(text)); } -static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getMetrics_utf16_next(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text)); } -static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getMetrics_utf16_prev(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text)); } -static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + const uint16_t* ptr = *(const uint16_t**)text; unsigned glyphID = *ptr; ptr += 1; @@ -617,11 +616,11 @@ static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache, const char** return cache->getGlyphIDMetrics(glyphID); } -static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + const uint16_t* ptr = *(const uint16_t**)text; ptr -= 1; unsigned glyphID = *ptr; @@ -629,45 +628,43 @@ static const SkGlyph& sk_getMetrics_glyph_prev(SkGlyphCache* cache, const char** return cache->getGlyphIDMetrics(glyphID); } -/// - -static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getAdvance_utf8_next(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharAdvance(SkUTF8_NextUnichar(text)); } -static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getAdvance_utf8_prev(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharAdvance(SkUTF8_PrevUnichar(text)); } -static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getAdvance_utf16_next(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharAdvance(SkUTF16_NextUnichar((const uint16_t**)text)); } -static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getAdvance_utf16_prev(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text)); } -static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + const uint16_t* ptr = *(const uint16_t**)text; unsigned glyphID = *ptr; ptr += 1; @@ -675,11 +672,11 @@ static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache, const char** return cache->getGlyphIDAdvance(glyphID); } -static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache, const char** text) -{ +static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache, + const char** text) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + const uint16_t* ptr = *(const uint16_t**)text; ptr -= 1; unsigned glyphID = *ptr; @@ -688,32 +685,33 @@ static const SkGlyph& sk_getAdvance_glyph_prev(SkGlyphCache* cache, const char** } SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd, - bool needFullMetrics) const -{ + bool needFullMetrics) const { static const SkMeasureCacheProc gMeasureCacheProcs[] = { sk_getMetrics_utf8_next, sk_getMetrics_utf16_next, sk_getMetrics_glyph_next, - + sk_getMetrics_utf8_prev, sk_getMetrics_utf16_prev, sk_getMetrics_glyph_prev, - + sk_getAdvance_utf8_next, sk_getAdvance_utf16_next, sk_getAdvance_glyph_next, - + sk_getAdvance_utf8_prev, sk_getAdvance_utf16_prev, sk_getAdvance_glyph_prev }; - + unsigned index = this->getTextEncoding(); - if (kBackward_TextBufferDirection == tbd) + if (kBackward_TextBufferDirection == tbd) { index += 3; - if (!needFullMetrics && !this->isDevKernText()) + } + if (!needFullMetrics && !this->isDevKernText()) { index += 6; + } SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs)); return gMeasureCacheProcs[index]; @@ -722,48 +720,43 @@ SkMeasureCacheProc SkPaint::getMeasureCacheProc(TextBufferDirection tbd, /////////////////////////////////////////////////////////////////////////////// static const SkGlyph& sk_getMetrics_utf8_00(SkGlyphCache* cache, - const char** text, SkFixed, SkFixed) -{ + const char** text, SkFixed, SkFixed) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharMetrics(SkUTF8_NextUnichar(text)); } static const SkGlyph& sk_getMetrics_utf8_xy(SkGlyphCache* cache, - const char** text, SkFixed x, SkFixed y) -{ + const char** text, SkFixed x, SkFixed y) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharMetrics(SkUTF8_NextUnichar(text), x, y); } -static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache, const char** text, - SkFixed, SkFixed) -{ +static const SkGlyph& sk_getMetrics_utf16_00(SkGlyphCache* cache, + const char** text, SkFixed, SkFixed) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text)); } static const SkGlyph& sk_getMetrics_utf16_xy(SkGlyphCache* cache, - const char** text, SkFixed x, SkFixed y) -{ + const char** text, SkFixed x, SkFixed y) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + return cache->getUnicharMetrics(SkUTF16_NextUnichar((const uint16_t**)text), x, y); } -static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache, const char** text, - SkFixed, SkFixed) -{ +static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache, + const char** text, SkFixed, SkFixed) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + const uint16_t* ptr = *(const uint16_t**)text; unsigned glyphID = *ptr; ptr += 1; @@ -772,11 +765,10 @@ static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache, const char** t } static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache, - const char** text, SkFixed x, SkFixed y) -{ + const char** text, SkFixed x, SkFixed y) { SkASSERT(cache != NULL); SkASSERT(text != NULL); - + const uint16_t* ptr = *(const uint16_t**)text; unsigned glyphID = *ptr; ptr += 1; @@ -784,22 +776,22 @@ static const SkGlyph& sk_getMetrics_glyph_xy(SkGlyphCache* cache, return cache->getGlyphIDMetrics(glyphID, x, y); } -SkDrawCacheProc SkPaint::getDrawCacheProc() const -{ +SkDrawCacheProc SkPaint::getDrawCacheProc() const { static const SkDrawCacheProc gDrawCacheProcs[] = { sk_getMetrics_utf8_00, sk_getMetrics_utf16_00, sk_getMetrics_glyph_00, - + sk_getMetrics_utf8_xy, sk_getMetrics_utf16_xy, sk_getMetrics_glyph_xy }; - + unsigned index = this->getTextEncoding(); - if (fFlags & kSubpixelText_Flag) + if (fFlags & kSubpixelText_Flag) { index += 3; - + } + SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs)); return gDrawCacheProcs[index]; } @@ -808,26 +800,25 @@ SkDrawCacheProc SkPaint::getDrawCacheProc() const class SkAutoRestorePaintTextSizeAndFrame { public: - SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint) : fPaint((SkPaint*)paint) - { + SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint) + : fPaint((SkPaint*)paint) { fTextSize = paint->getTextSize(); fStyle = paint->getStyle(); fPaint->setStyle(SkPaint::kFill_Style); } - ~SkAutoRestorePaintTextSizeAndFrame() - { + + ~SkAutoRestorePaintTextSizeAndFrame() { fPaint->setStyle(fStyle); fPaint->setTextSize(fTextSize); } - + private: SkPaint* fPaint; SkScalar fTextSize; SkPaint::Style fStyle; }; -static void set_bounds(const SkGlyph& g, SkRect* bounds) -{ +static void set_bounds(const SkGlyph& g, SkRect* bounds) { bounds->set(SkIntToScalar(g.fLeft), SkIntToScalar(g.fTop), SkIntToScalar(g.fLeft + g.fWidth), @@ -840,7 +831,7 @@ typedef int64_t Sk48Dot16; #ifdef SK_SCALAR_IS_FLOAT static inline float Sk48Dot16ToScalar(Sk48Dot16 x) { - return x * 1.5258789e-5f; // x * (1 / 65536.0f) + return (float) (x * 1.5258789e-5); // x * (1 / 65536.0f) } #else static inline SkFixed Sk48Dot16ToScalar(Sk48Dot16 x) { @@ -849,8 +840,7 @@ typedef int64_t Sk48Dot16; } #endif -static void join_bounds(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) -{ +static void join_bounds(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) { SkScalar sx = Sk48Dot16ToScalar(dx); bounds->join(SkIntToScalar(g.fLeft) + sx, SkIntToScalar(g.fTop), @@ -860,14 +850,13 @@ static void join_bounds(const SkGlyph& g, SkRect* bounds, Sk48Dot16 dx) SkScalar SkPaint::measure_text(SkGlyphCache* cache, const char* text, size_t byteLength, - int* count, SkRect* bounds) const -{ + int* count, SkRect* bounds) const { SkASSERT(count); - if (byteLength == 0) - { + if (byteLength == 0) { *count = 0; - if (bounds) + if (bounds) { bounds->setEmpty(); + } return 0; } @@ -885,29 +874,22 @@ SkScalar SkPaint::measure_text(SkGlyphCache* cache, SkAutoKern autokern; - if (NULL == bounds) - { - if (this->isDevKernText()) - { + if (NULL == bounds) { + if (this->isDevKernText()) { int rsb; for (; text < stop; n++) { rsb = g->fRsbDelta; g = &glyphCacheProc(cache, &text); x += SkAutoKern_AdjustF(rsb, g->fLsbDelta) + g->fAdvanceX; } - } - else - { + } else { for (; text < stop; n++) { x += glyphCacheProc(cache, &text).fAdvanceX; } } - } - else - { + } else { set_bounds(*g, bounds); - if (this->isDevKernText()) - { + if (this->isDevKernText()) { int rsb; for (; text < stop; n++) { rsb = g->fRsbDelta; @@ -916,9 +898,7 @@ SkScalar SkPaint::measure_text(SkGlyphCache* cache, join_bounds(*g, bounds, x); x += g->fAdvanceX; } - } - else - { + } else { for (; text < stop; n++) { g = &glyphCacheProc(cache, &text); join_bounds(*g, bounds, x); @@ -933,24 +913,21 @@ SkScalar SkPaint::measure_text(SkGlyphCache* cache, } SkScalar SkPaint::measureText(const void* textData, size_t length, - SkRect* bounds, SkScalar zoom) const -{ + SkRect* bounds, SkScalar zoom) const { const char* text = (const char*)textData; SkASSERT(text != NULL || length == 0); SkScalar scale = 0; SkAutoRestorePaintTextSizeAndFrame restore(this); - if (this->isLinearText()) - { + if (this->isLinearText()) { scale = fTextSize / kCanonicalTextSizeForPaths; // this gets restored by restore ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); } - - SkMatrix zoomMatrix, *zoomPtr = NULL; - if (zoom) - { + + SkMatrix zoomMatrix, *zoomPtr = NULL; + if (zoom) { zoomMatrix.setScale(zoom, zoom); zoomPtr = &zoomMatrix; } @@ -959,17 +936,14 @@ SkScalar SkPaint::measureText(const void* textData, size_t length, SkGlyphCache* cache = autoCache.getCache(); SkScalar width = 0; - - if (length > 0) - { + + if (length > 0) { int tempCount; width = this->measure_text(cache, text, length, &tempCount, bounds); - if (scale) - { + if (scale) { width = SkScalarMul(width, scale); - if (bounds) - { + if (bounds) { bounds->fLeft = SkScalarMul(bounds->fLeft, scale); bounds->fTop = SkScalarMul(bounds->fTop, scale); bounds->fRight = SkScalarMul(bounds->fRight, scale); @@ -982,26 +956,21 @@ SkScalar SkPaint::measureText(const void* textData, size_t length, typedef bool (*SkTextBufferPred)(const char* text, const char* stop); -static bool forward_textBufferPred(const char* text, const char* stop) -{ +static bool forward_textBufferPred(const char* text, const char* stop) { return text < stop; } -static bool backward_textBufferPred(const char* text, const char* stop) -{ +static bool backward_textBufferPred(const char* text, const char* stop) { return text > stop; } static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd, - const char** text, size_t length, const char** stop) -{ - if (SkPaint::kForward_TextBufferDirection == tbd) - { + const char** text, size_t length, + const char** stop) { + if (SkPaint::kForward_TextBufferDirection == tbd) { *stop = *text + length; return forward_textBufferPred; - } - else - { + } else { // text should point to the end of the buffer, and stop to the beginning *stop = *text; *text += length; @@ -1011,12 +980,11 @@ static SkTextBufferPred chooseTextBufferPred(SkPaint::TextBufferDirection tbd, size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, SkScalar* measuredWidth, - TextBufferDirection tbd) const -{ - if (0 == length || 0 >= maxWidth) - { - if (measuredWidth) + TextBufferDirection tbd) const { + if (0 == length || 0 >= maxWidth) { + if (measuredWidth) { *measuredWidth = 0; + } return 0; } @@ -1026,14 +994,13 @@ size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, SkScalar scale = 0; SkAutoRestorePaintTextSizeAndFrame restore(this); - if (this->isLinearText()) - { + if (this->isLinearText()) { scale = fTextSize / kCanonicalTextSizeForPaths; maxWidth = SkScalarMulDiv(maxWidth, kCanonicalTextSizeForPaths, fTextSize); // this gets restored by restore ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); } - + SkAutoGlyphCache autoCache(*this, NULL); SkGlyphCache* cache = autoCache.getCache(); @@ -1046,47 +1013,39 @@ size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, SkAutoKern autokern; - if (this->isDevKernText()) - { + if (this->isDevKernText()) { int rsb = 0; - while (pred(text, stop)) - { + while (pred(text, stop)) { const char* curr = text; const SkGlyph& g = glyphCacheProc(cache, &text); SkFixed x = SkAutoKern_AdjustF(rsb, g.fLsbDelta) + g.fAdvanceX; - if ((width += x) > max) - { + if ((width += x) > max) { width -= x; text = curr; break; } rsb = g.fRsbDelta; } - } - else - { - while (pred(text, stop)) - { + } else { + while (pred(text, stop)) { const char* curr = text; SkFixed x = glyphCacheProc(cache, &text).fAdvanceX; - if ((width += x) > max) - { + if ((width += x) > max) { width -= x; text = curr; break; } } } - - if (measuredWidth) - { - + + if (measuredWidth) { SkScalar scalarWidth = Sk48Dot16ToScalar(width); - if (scale) + if (scale) { scalarWidth = SkScalarMul(scalarWidth, scale); + } *measuredWidth = scalarWidth; } - + // return the number of bytes measured return (kForward_TextBufferDirection == tbd) ? text - stop + length : stop - text + length; @@ -1094,32 +1053,27 @@ size_t SkPaint::breakText(const void* textD, size_t length, SkScalar maxWidth, /////////////////////////////////////////////////////////////////////////////// -static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) -{ +static bool FontMetricsCacheProc(const SkGlyphCache* cache, void* context) { *(SkPaint::FontMetrics*)context = cache->getFontMetricsY(); return false; // don't detach the cache } -static void FontMetricsDescProc(const SkDescriptor* desc, void* context) -{ +static void FontMetricsDescProc(const SkDescriptor* desc, void* context) { SkGlyphCache::VisitCache(desc, FontMetricsCacheProc, context); } -SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const -{ +SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const { SkScalar scale = 0; SkAutoRestorePaintTextSizeAndFrame restore(this); - if (this->isLinearText()) - { + if (this->isLinearText()) { scale = fTextSize / kCanonicalTextSizeForPaths; // this gets restored by restore ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); } - - SkMatrix zoomMatrix, *zoomPtr = NULL; - if (zoom) - { + + SkMatrix zoomMatrix, *zoomPtr = NULL; + if (zoom) { zoomMatrix.setScale(zoom, zoom); zoomPtr = &zoomMatrix; } @@ -1130,13 +1084,13 @@ SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const const FontMetrics& my = cache->getFontMetricsY(); #endif FontMetrics storage; - if (NULL == metrics) + if (NULL == metrics) { metrics = &storage; - + } + this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics); - if (scale) - { + if (scale) { metrics->fTop = SkScalarMul(metrics->fTop, scale); metrics->fAscent = SkScalarMul(metrics->fAscent, scale); metrics->fDescent = SkScalarMul(metrics->fDescent, scale); @@ -1146,32 +1100,31 @@ SkScalar SkPaint::getFontMetrics(FontMetrics* metrics, SkScalar zoom) const return metrics->fDescent - metrics->fAscent + metrics->fLeading; } -//////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) -{ +static void set_bounds(const SkGlyph& g, SkRect* bounds, SkScalar scale) { bounds->set(g.fLeft * scale, g.fTop * scale, (g.fLeft + g.fWidth) * scale, (g.fTop + g.fHeight) * scale); } -int SkPaint::getTextWidths(const void* textData, size_t byteLength, SkScalar widths[], - SkRect bounds[]) const -{ - if (0 == byteLength) +int SkPaint::getTextWidths(const void* textData, size_t byteLength, + SkScalar widths[], SkRect bounds[]) const { + if (0 == byteLength) { return 0; + } SkASSERT(NULL != textData); - if (NULL == widths && NULL == bounds) + if (NULL == widths && NULL == bounds) { return this->countText(textData, byteLength); + } SkAutoRestorePaintTextSizeAndFrame restore(this); SkScalar scale = 0; - if (this->isLinearText()) - { + if (this->isLinearText()) { scale = fTextSize / kCanonicalTextSizeForPaths; // this gets restored by restore ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths)); @@ -1187,8 +1140,7 @@ int SkPaint::getTextWidths(const void* textData, size_t byteLength, SkScalar wid const char* stop = text + byteLength; int count = 0; - if (this->isDevKernText()) - { + if (this->isDevKernText()) { // we adjust the widths returned here through auto-kerning SkAutoKern autokern; SkFixed prevWidth = 0; @@ -1264,16 +1216,18 @@ int SkPaint::getTextWidths(const void* textData, size_t byteLength, SkScalar wid return count; } -//////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// #include "SkDraw.h" -void SkPaint::getTextPath(const void* textData, size_t length, SkScalar x, SkScalar y, SkPath* path) const -{ +void SkPaint::getTextPath(const void* textData, size_t length, + SkScalar x, SkScalar y, SkPath* path) const { + SkASSERT(length == 0 || textData != NULL); + const char* text = (const char*)textData; - SkASSERT(length == 0 || text != NULL); - if (text == NULL || length == 0 || path == NULL) + if (text == NULL || length == 0 || path == NULL) { return; + } SkTextToPathIter iter(text, length, *this, false, true); SkMatrix matrix; @@ -1285,8 +1239,7 @@ void SkPaint::getTextPath(const void* textData, size_t length, SkScalar x, SkSca SkScalar xpos; const SkPath* iterPath; - while ((iterPath = iter.next(&xpos)) != NULL) - { + while ((iterPath = iter.next(&xpos)) != NULL) { matrix.postTranslate(xpos - prevXPos, 0); path->addPath(*iterPath, matrix); prevXPos = xpos; @@ -1298,51 +1251,7 @@ static void add_flattenable(SkDescriptor* desc, uint32_t tag, buffer->flatten(desc->addEntry(tag, buffer->size(), NULL)); } -/* - * interpolates to find the right value for key, in the function represented by the 'length' number of pairs: (keys[i], values[i]) - inspired by a desire to change the multiplier for thickness in fakebold - therefore, i assumed number of pairs (length) will be small, so a linear search is sufficient - repeated keys are allowed for discontinuous functions (so long as keys is monotonically increasing), and if - key is the value of a repeated scalar in keys, the first one will be used - - this may change if a binary search is used - - also, this ensures that there is no divide by zero (an assert also checks for that) -*/ -static SkScalar interpolate(SkScalar key, const SkScalar keys[], const SkScalar values[], int length) -{ - - SkASSERT(length > 0); - SkASSERT(keys != NULL); - SkASSERT(values != NULL); -#ifdef SK_DEBUG - for (int i = 1; i < length; i++) - SkASSERT(keys[i] >= keys[i-1]); -#endif - int right = 0; - while (right < length && key > keys[right]) - right++; - //could use sentinal values to eliminate conditionals - //i assume i am not in control of input values, so i want to make it simple - if (length == right) - return values[length-1]; - if (0 == right) - return values[0]; - //otherwise, we interpolate between right-1 and right - SkScalar rVal = values[right]; - SkScalar lVal = values[right-1]; - SkScalar rightKey = keys[right]; - SkScalar leftKey = keys[right-1]; - SkASSERT(rightKey != leftKey); - //fractional amount which we will multiply by the difference in the left value and right value - SkScalar fract = SkScalarDiv(key-leftKey,rightKey-leftKey); - return lVal + SkScalarMul(fract, rVal-lVal); -} - -//used for interpolating in fakeBold -static const SkScalar pointSizes[] = { SkIntToScalar(9), SkIntToScalar(36) }; -static const SkScalar multipliers[] = { SK_Scalar1/24, SK_Scalar1/32 }; - -static SkMask::Format computeMaskFormat(const SkPaint& paint) -{ +static SkMask::Format computeMaskFormat(const SkPaint& paint) { uint32_t flags = paint.getFlags(); // Antialiasing being disabled trumps all other settings. @@ -1350,9 +1259,10 @@ static SkMask::Format computeMaskFormat(const SkPaint& paint) return SkMask::kBW_Format; #if defined(SK_SUPPORT_LCDTEXT) - if (flags & SkPaint::kLCDRenderText_Flag) + if (flags & SkPaint::kLCDRenderText_Flag) { return SkFontHost::GetSubpixelOrientation() == SkFontHost::kHorizontal_LCDOrientation ? SkMask::kHorizontalLCD_Format : SkMask::kVerticalLCD_Format; + } #endif return SkMask::kA8_Format; @@ -1368,9 +1278,23 @@ static SkPaint::Hinting computeHinting(const SkPaint& paint) { return h; } +/* + * Return the scalar with only limited fractional precision. Used to consolidate matrices + * that vary only slightly when we create our key into the font cache, since the font scaler + * typically returns the same looking resuts for tiny changes in the matrix. + */ +static SkScalar sk_relax(SkScalar x) { +#ifdef SK_SCALAR_IS_FLOAT + int n = sk_float_round2int(x * 1024); + return n / 1024.0f; +#else + // round to the nearest 10 fractional bits + return (x + (1 << 5)) & ~(1024 - 1); +#endif +} + void SkScalerContext::MakeRec(const SkPaint& paint, - const SkMatrix* deviceMatrix, Rec* rec) -{ + const SkMatrix* deviceMatrix, Rec* rec) { SkASSERT(deviceMatrix == NULL || (deviceMatrix->getType() & SkMatrix::kPerspective_Mask) == 0); @@ -1379,67 +1303,70 @@ void SkScalerContext::MakeRec(const SkPaint& paint, rec->fPreScaleX = paint.getTextScaleX(); rec->fPreSkewX = paint.getTextSkewX(); - if (deviceMatrix) - { - rec->fPost2x2[0][0] = deviceMatrix->getScaleX(); - rec->fPost2x2[0][1] = deviceMatrix->getSkewX(); - rec->fPost2x2[1][0] = deviceMatrix->getSkewY(); - rec->fPost2x2[1][1] = deviceMatrix->getScaleY(); - } - else - { + if (deviceMatrix) { + rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX()); + rec->fPost2x2[0][1] = sk_relax(deviceMatrix->getSkewX()); + rec->fPost2x2[1][0] = sk_relax(deviceMatrix->getSkewY()); + rec->fPost2x2[1][1] = sk_relax(deviceMatrix->getScaleY()); + } else { rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1; rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0; } - + SkPaint::Style style = paint.getStyle(); SkScalar strokeWidth = paint.getStrokeWidth(); - + unsigned flags = SkFontHost::ComputeGammaFlag(paint); - if (paint.isFakeBoldText()) - { + if (paint.isFakeBoldText()) { #ifdef SK_USE_FREETYPE_EMBOLDEN flags |= SkScalerContext::kEmbolden_Flag; #else - SkScalar fakeBoldScale = interpolate(paint.getTextSize(), pointSizes, multipliers, 2); + SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(), + kStdFakeBoldInterpKeys, + kStdFakeBoldInterpValues, + kStdFakeBoldInterpLength); SkScalar extra = SkScalarMul(paint.getTextSize(), fakeBoldScale); - - if (style == SkPaint::kFill_Style) - { + + if (style == SkPaint::kFill_Style) { style = SkPaint::kStrokeAndFill_Style; strokeWidth = extra; // ignore paint's strokeWidth if it was "fill" - } - else + } else { strokeWidth += extra; + } #endif } - if (paint.isDevKernText()) + if (paint.isDevKernText()) { flags |= SkScalerContext::kDevKernText_Flag; - - if (style != SkPaint::kFill_Style && strokeWidth > 0) - { + } + + if (style != SkPaint::kFill_Style && strokeWidth > 0) { rec->fFrameWidth = strokeWidth; rec->fMiterLimit = paint.getStrokeMiter(); rec->fStrokeJoin = SkToU8(paint.getStrokeJoin()); - if (style == SkPaint::kStrokeAndFill_Style) + if (style == SkPaint::kStrokeAndFill_Style) { flags |= SkScalerContext::kFrameAndFill_Flag; - } - else - { + } + } else { rec->fFrameWidth = 0; rec->fMiterLimit = 0; rec->fStrokeJoin = 0; } - rec->fSubpixelPositioning = paint.isSubpixelText(); rec->fMaskFormat = SkToU8(computeMaskFormat(paint)); rec->fFlags = SkToU8(flags); rec->setHinting(computeHinting(paint)); - if (paint.isEmbeddedBitmapText()) + if (paint.isEmbeddedBitmapText()) { rec->fFlags |= SkScalerContext::kEmbeddedBitmapText_Flag; + } + if (paint.isSubpixelText()) { + rec->fFlags |= SkScalerContext::kSubpixelPositioning_Flag; + } + if (paint.isAutohinted()) { + rec->fFlags |= SkScalerContext::kAutohinting_Flag; + } /* 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, @@ -1453,8 +1380,7 @@ void SkScalerContext::MakeRec(const SkPaint& paint, void SkPaint::descriptorProc(const SkMatrix* deviceMatrix, void (*proc)(const SkDescriptor*, void*), - void* context, bool ignoreGamma) const -{ + void* context, bool ignoreGamma) const { SkScalerContext::Rec rec; SkScalerContext::MakeRec(*this, deviceMatrix, &rec); @@ -1516,8 +1442,7 @@ void SkPaint::descriptorProc(const SkMatrix* deviceMatrix, proc(desc, context); } -SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const -{ +SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const { SkGlyphCache* cache; this->descriptorProc(deviceMatrix, DetachDescProc, &cache); return cache; @@ -1587,7 +1512,7 @@ void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const { asint(this->getLooper())) { flatFlags |= kHasEffects_FlatFlag; } - + SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize); uint32_t* ptr = buffer.reserve(kPODPaintSize); @@ -1621,13 +1546,13 @@ void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) { SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize); const void* podData = buffer.skip(kPODPaintSize); const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData); - + // the order we read must match the order we wrote in flatten() this->setTextSize(read_scalar(pod)); this->setTextScaleX(read_scalar(pod)); this->setTextSkewX(read_scalar(pod)); this->setStrokeWidth(read_scalar(pod)); - this->setStrokeMiter(read_scalar(pod)); + this->setStrokeMiter(read_scalar(pod)); this->setColor(*pod++); uint32_t tmp = *pod++; @@ -1648,13 +1573,13 @@ void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) { } if (flatFlags & kHasEffects_FlatFlag) { - this->setPathEffect((SkPathEffect*) buffer.readFlattenable())->safeUnref(); - this->setShader((SkShader*) buffer.readFlattenable())->safeUnref(); - this->setXfermode((SkXfermode*) buffer.readFlattenable())->safeUnref(); - this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable())->safeUnref(); - this->setColorFilter((SkColorFilter*) buffer.readFlattenable())->safeUnref(); - this->setRasterizer((SkRasterizer*) buffer.readFlattenable())->safeUnref(); - this->setLooper((SkDrawLooper*) buffer.readFlattenable())->safeUnref(); + SkSafeUnref(this->setPathEffect((SkPathEffect*) buffer.readFlattenable())); + SkSafeUnref(this->setShader((SkShader*) buffer.readFlattenable())); + SkSafeUnref(this->setXfermode((SkXfermode*) buffer.readFlattenable())); + SkSafeUnref(this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable())); + SkSafeUnref(this->setColorFilter((SkColorFilter*) buffer.readFlattenable())); + SkSafeUnref(this->setRasterizer((SkRasterizer*) buffer.readFlattenable())); + SkSafeUnref(this->setLooper((SkDrawLooper*) buffer.readFlattenable())); } else { this->setPathEffect(NULL); this->setShader(NULL); @@ -1720,60 +1645,59 @@ SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) return filter; } -//////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const -{ +bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const { SkPath effectPath, strokePath; const SkPath* path = &src; SkScalar width = this->getStrokeWidth(); - + switch (this->getStyle()) { - case SkPaint::kFill_Style: - width = -1; // mark it as no-stroke - break; - case SkPaint::kStrokeAndFill_Style: - if (width == 0) + case SkPaint::kFill_Style: width = -1; // mark it as no-stroke - break; - case SkPaint::kStroke_Style: - break; - default: - SkASSERT(!"unknown paint style"); + break; + case SkPaint::kStrokeAndFill_Style: + if (width == 0) { + width = -1; // mark it as no-stroke + } + break; + case SkPaint::kStroke_Style: + break; + default: + SkASSERT(!"unknown paint style"); } - if (this->getPathEffect()) - { + if (this->getPathEffect()) { // lie to the pathEffect if our style is strokeandfill, so that it treats us as just fill - if (this->getStyle() == SkPaint::kStrokeAndFill_Style) + if (this->getStyle() == SkPaint::kStrokeAndFill_Style) { width = -1; // mark it as no-stroke + } - if (this->getPathEffect()->filterPath(&effectPath, src, &width)) + if (this->getPathEffect()->filterPath(&effectPath, src, &width)) { path = &effectPath; - + } + // restore the width if we earlier had to lie, and if we're still set to no-stroke // note: if we're now stroke (width >= 0), then the pathEffect asked for that change // and we want to respect that (i.e. don't overwrite their setting for width) - if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) - { + if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) { width = this->getStrokeWidth(); - if (width == 0) + if (width == 0) { width = -1; + } } } - - if (width > 0 && !path->isEmpty()) - { + + if (width > 0 && !path->isEmpty()) { SkStroke stroker(*this, width); stroker.strokePath(*path, &strokePath); path = &strokePath; } - if (path == &src) + if (path == &src) { *dst = src; - else - { + } else { SkASSERT(path == &effectPath || path == &strokePath); dst->swap(*(SkPath*)path); } @@ -1801,40 +1725,38 @@ const SkRect& SkPaint::computeStrokeFastBounds(const SkRect& src, return *storage; } -//////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -static bool has_thick_frame(const SkPaint& paint) -{ - return paint.getStrokeWidth() > 0 && paint.getStyle() != SkPaint::kFill_Style; +static bool has_thick_frame(const SkPaint& paint) { + return paint.getStrokeWidth() > 0 && + paint.getStyle() != SkPaint::kFill_Style; } SkTextToPathIter::SkTextToPathIter( const char text[], size_t length, const SkPaint& paint, bool applyStrokeAndPathEffects, - bool forceLinearTextOn) - : fPaint(paint) /* make a copy of the paint */ -{ + bool forceLinearTextOn) : fPaint(paint) { fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection, true); - if (forceLinearTextOn) + if (forceLinearTextOn) { fPaint.setLinearText(true); + } fPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup - if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) + if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) { applyStrokeAndPathEffects = false; + } // can't use our canonical size if we need to apply patheffects/strokes - if (fPaint.isLinearText() && !applyStrokeAndPathEffects) - { + if (fPaint.isLinearText() && !applyStrokeAndPathEffects) { fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; - } - else + } else { fScale = SK_Scalar1; - - if (!applyStrokeAndPathEffects) - { + } + + if (!applyStrokeAndPathEffects) { fPaint.setStyle(SkPaint::kFill_Style); fPaint.setPathEffect(NULL); } @@ -1844,8 +1766,7 @@ SkTextToPathIter::SkTextToPathIter( const char text[], size_t length, SkPaint::Style style = SkPaint::kFill_Style; SkPathEffect* pe = NULL; - if (!applyStrokeAndPathEffects) - { + if (!applyStrokeAndPathEffects) { style = paint.getStyle(); // restore pe = paint.getPathEffect(); // restore } @@ -1856,12 +1777,13 @@ SkTextToPathIter::SkTextToPathIter( const char text[], size_t length, // now compute fXOffset if needed SkScalar xOffset = 0; - if (paint.getTextAlign() != SkPaint::kLeft_Align) // need to measure first - { + if (paint.getTextAlign() != SkPaint::kLeft_Align) { // need to measure first int count; - SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length, &count, NULL), fScale); - if (paint.getTextAlign() == SkPaint::kCenter_Align) + SkScalar width = SkScalarMul(fPaint.measure_text(fCache, text, length, + &count, NULL), fScale); + if (paint.getTextAlign() == SkPaint::kCenter_Align) { width = SkScalarHalf(width); + } xOffset = -width; } fXPos = xOffset; @@ -1871,24 +1793,21 @@ SkTextToPathIter::SkTextToPathIter( const char text[], size_t length, fStop = text + length; } -SkTextToPathIter::~SkTextToPathIter() -{ +SkTextToPathIter::~SkTextToPathIter() { SkGlyphCache::AttachCache(fCache); } -const SkPath* SkTextToPathIter::next(SkScalar* xpos) -{ - while (fText < fStop) - { +const SkPath* SkTextToPathIter::next(SkScalar* xpos) { + while (fText < fStop) { const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText); fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale); fPrevAdvance = glyph.fAdvanceX; // + fPaint.getTextTracking(); - if (glyph.fWidth) - { - if (xpos) + if (glyph.fWidth) { + if (xpos) { *xpos = fXPos; + } return fCache->findPath(glyph); } } diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index 62edb32..4962239 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -24,11 +24,11 @@ /* This guy's constructor/destructor bracket a path editing operation. It is used when we know the bounds of the amount we are going to add to the path (usually a new contour, but not required). - + It captures some state about the path up front (i.e. if it already has a cached bounds), and the if it can, it updates the cache bounds explicitly, avoiding the need to revisit all of the points in getBounds(). - + It also notes if the path was originally empty, and if so, sets isConvex to true. Thus it can only be used if the contour being added is convex. */ @@ -43,7 +43,7 @@ public: fRect.set(left, top, right, bottom); this->init(path); } - + ~SkAutoPathBoundsUpdate() { fPath->setIsConvex(fEmpty); if (fEmpty) { @@ -54,13 +54,13 @@ public: fPath->fBoundsIsDirty = false; } } - + private: SkPath* fPath; SkRect fRect; bool fDirty; bool fEmpty; - + // returns true if we should proceed void init(SkPath* path) { fPath = path; @@ -177,7 +177,7 @@ bool SkPath::isEmpty() const { bool SkPath::isRect(SkRect*) const { SkDEBUGCODE(this->validate();) - + SkASSERT(!"unimplemented"); return false; } @@ -358,7 +358,7 @@ void SkPath::close() { } /////////////////////////////////////////////////////////////////////////////// - + void SkPath::addRect(const SkRect& rect, Direction dir) { this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir); } @@ -386,8 +386,6 @@ void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, Direction dir) { - SkAutoPathBoundsUpdate apbu(this, rect); - SkScalar w = rect.width(); SkScalar halfW = SkScalarHalf(w); SkScalar h = rect.height(); @@ -397,13 +395,16 @@ void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, return; } - bool skip_hori = rx >= halfW; - bool skip_vert = ry >= halfH; + bool skip_hori = rx >= halfW; + bool skip_vert = ry >= halfH; if (skip_hori && skip_vert) { this->addOval(rect, dir); return; } + + SkAutoPathBoundsUpdate apbu(this, rect); + if (skip_hori) { rx = halfW; } else if (skip_vert) { @@ -474,7 +475,7 @@ static void add_corner_arc(SkPath* path, const SkRect& rect, SkPath::Direction dir, bool forceMoveTo) { rx = SkMinScalar(SkScalarHalf(rect.width()), rx); ry = SkMinScalar(SkScalarHalf(rect.height()), ry); - + SkRect r; r.set(-rx, -ry, rx, ry); @@ -489,19 +490,24 @@ static void add_corner_arc(SkPath* path, const SkRect& rect, case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break; default: SkASSERT(!"unexpected startAngle in add_corner_arc"); } - + SkScalar start = SkIntToScalar(startAngle); SkScalar sweep = SkIntToScalar(90); if (SkPath::kCCW_Direction == dir) { start += sweep; sweep = -sweep; } - + path->arcTo(r, start, sweep, forceMoveTo); } void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[], Direction dir) { + // abort before we invoke SkAutoPathBoundsUpdate() + if (rect.isEmpty()) { + return; + } + SkAutoPathBoundsUpdate apbu(this, rect); if (kCW_Direction == dir) { @@ -626,10 +632,10 @@ static int build_arc_points(const SkRect& oval, SkScalar startAngle, } SkMatrix matrix; - + matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); matrix.postTranslate(oval.centerX(), oval.centerY()); - + return SkBuildQuadArc(start, stop, sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection, &matrix, pts); @@ -685,22 +691,31 @@ void SkPath::addArc(const SkRect& oval, SkScalar startAngle, void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) { SkVector before, after; - + // need to know our prev pt so we can construct tangent vectors { SkPoint start; this->getLastPt(&start); + // Handle degenerate cases by adding a line to the first point and + // bailing out. + if ((x1 == start.fX && y1 == start.fY) || + (x1 == x2 && y1 == y2) || + radius == 0) { + this->lineTo(x1, y1); + return; + } before.setNormalize(x1 - start.fX, y1 - start.fY); after.setNormalize(x2 - x1, y2 - y1); } - + SkScalar cosh = SkPoint::DotProduct(before, after); SkScalar sinh = SkPoint::CrossProduct(before, after); if (SkScalarNearlyZero(sinh)) { // angle is too tight + this->lineTo(x1, y1); return; } - + SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh); if (dist < 0) { dist = -dist; @@ -723,13 +738,13 @@ void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkMatrix matrix; SkPoint pts[kSkBuildQuadArcStorage]; - + matrix.setScale(radius, radius); matrix.postTranslate(xx - SkScalarMul(radius, before.fX), yy - SkScalarMul(radius, before.fY)); - + int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts); - + this->incReserve(count); // [xx,yy] == pts[0] this->lineTo(xx, yy); @@ -1009,13 +1024,13 @@ bool SkPath::Iter::isClosedContour() const { const uint8_t* verbs = fVerbs; const uint8_t* stop = fVerbStop; - + if (kMove_Verb == *verbs) { verbs += 1; // skip the initial moveto } while (verbs < stop) { - unsigned v = *verbs++; + unsigned v = *verbs++; if (kMove_Verb == v) { break; } @@ -1338,7 +1353,7 @@ void SkPath::validate() const { SkASSERT((fFillType & ~3) == 0); fPts.validate(); fVerbs.validate(); - + if (!fBoundsIsDirty) { SkRect bounds; compute_pt_bounds(&bounds, fPts); diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp index 343ca2b..c199cae 100644 --- a/src/core/SkPicture.cpp +++ b/src/core/SkPicture.cpp @@ -2,16 +2,16 @@ ** ** Copyright 2007, 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 +** 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 +** 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 +** 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. */ @@ -37,7 +37,7 @@ #ifdef SK_DEBUG -// enable SK_DEBUG_TRACE to trace DrawType elements when +// enable SK_DEBUG_TRACE to trace DrawType elements when // recorded and played back // #define SK_DEBUG_TRACE // enable SK_DEBUG_SIZE to see the size of picture components @@ -77,12 +77,12 @@ const char* DrawTypeToString(DrawType drawType) { case SCALE: return "SCALE"; case SKEW: return "SKEW"; case TRANSLATE: return "TRANSLATE"; - default: - SkDebugf("DrawType error 0x%08x\n", drawType); - SkASSERT(0); + default: + SkDebugf("DrawType error 0x%08x\n", drawType); + SkASSERT(0); break; } - SkASSERT(0); + SkASSERT(0); return NULL; } #endif @@ -133,7 +133,7 @@ SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() { } SkPicture::~SkPicture() { - fRecord->safeUnref(); + SkSafeUnref(fRecord); SkDELETE(fPlayback); } @@ -166,7 +166,7 @@ SkCanvas* SkPicture::beginRecording(int width, int height, SkBitmap bm; bm.setConfig(SkBitmap::kNo_Config, width, height); fRecord->setBitmapDevice(bm); - + return fRecord; } @@ -217,7 +217,7 @@ SkPicture::SkPicture(SkStream* stream) : SkRefCnt() { void SkPicture::serialize(SkWStream* stream) const { SkPicturePlayback* playback = fPlayback; - + if (NULL == playback && fRecord) { playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); } diff --git a/include/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index 2c0af5a..2c0af5a 100644 --- a/include/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index c73e21c..4a4c7f8 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -14,7 +14,7 @@ SkPicturePlayback::SkPicturePlayback() { SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { #ifdef SK_DEBUG_SIZE - size_t overallBytes, bitmapBytes, matricesBytes, + size_t overallBytes, bitmapBytes, matricesBytes, paintBytes, pathBytes, pictureBytes, regionBytes; int bitmaps = record.bitmaps(&bitmapBytes); int matrices = record.matrices(&matricesBytes); @@ -42,7 +42,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { SkDebugf("rects size %zd (rects:%d) ", record.fRectBytes, record.fRectWrites); if (record.fTextWrites != 0) SkDebugf("text size %zd (text strings:%d) ", record.fTextBytes, record.fTextWrites); - + SkDebugf("\n"); #endif #ifdef SK_DEBUG_DUMP @@ -55,27 +55,27 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { init(); if (writer.size() == 0) return; - + { size_t size = writer.size(); void* buffer = sk_malloc_throw(size); writer.flatten(buffer); fReader.setMemory(buffer, size); // fReader owns buffer now } - + // copy over the refcnt dictionary to our reader // fRCPlayback.reset(&record.fRCRecorder); fRCPlayback.setupBuffer(fReader); - + fTFPlayback.reset(&record.fTFRecorder); fTFPlayback.setupBuffer(fReader); - + const SkTDArray<const SkFlatBitmap* >& bitmaps = record.getBitmaps(); fBitmapCount = bitmaps.count(); if (fBitmapCount > 0) { fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount); - for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin(); + for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin(); flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) { const SkFlatBitmap* flatBitmap = *flatBitmapPtr; int index = flatBitmap->index() - 1; @@ -87,7 +87,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { fMatrixCount = matrices.count(); if (fMatrixCount > 0) { fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount); - for (const SkFlatMatrix** matrixPtr = matrices.begin(); + for (const SkFlatMatrix** matrixPtr = matrices.begin(); matrixPtr != matrices.end(); matrixPtr++) { const SkFlatMatrix* flatMatrix = *matrixPtr; flatMatrix->unflatten(&fMatrices[flatMatrix->index() - 1]); @@ -98,7 +98,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { fPaintCount = paints.count(); if (fPaintCount > 0) { fPaints = SkNEW_ARRAY(SkPaint, fPaintCount); - for (const SkFlatPaint** flatPaintPtr = paints.begin(); + for (const SkFlatPaint** flatPaintPtr = paints.begin(); flatPaintPtr != paints.end(); flatPaintPtr++) { const SkFlatPaint* flatPaint = *flatPaintPtr; int index = flatPaint->index() - 1; @@ -108,7 +108,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { } fPathHeap = record.fPathHeap; - fPathHeap->safeRef(); + SkSafeRef(fPathHeap); const SkTDArray<SkPicture* >& pictures = record.getPictureRefs(); fPictureCount = pictures.count(); @@ -119,7 +119,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { fPictureRefs[i]->ref(); } } - + const SkTDArray<SkShape* >& shapes = record.getShapes(); fShapeCount = shapes.count(); if (fShapeCount > 0) { @@ -130,18 +130,18 @@ SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) { fShapes[i] = s; } } - + const SkTDArray<const SkFlatRegion* >& regions = record.getRegions(); fRegionCount = regions.count(); if (fRegionCount > 0) { fRegions = SkNEW_ARRAY(SkRegion, fRegionCount); - for (const SkFlatRegion** flatRegionPtr = regions.begin(); + for (const SkFlatRegion** flatRegionPtr = regions.begin(); flatRegionPtr != regions.end(); flatRegionPtr++) { const SkFlatRegion* flatRegion = *flatRegionPtr; flatRegion->unflatten(&fRegions[flatRegion->index() - 1]); } } - + #ifdef SK_DEBUG_SIZE int overall = fPlayback->size(&overallBytes); bitmaps = fPlayback->bitmaps(&bitmapBytes); @@ -194,7 +194,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src) { } fPathHeap = src.fPathHeap; - fPathHeap->safeRef(); + SkSafeRef(fPathHeap); fPictureCount = src.fPictureCount; fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount); @@ -202,7 +202,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src) { fPictureRefs[i] = src.fPictureRefs[i]; fPictureRefs[i]->ref(); } - + fShapeCount = src.fShapeCount; fShapes = SkNEW_ARRAY(SkShape*, fShapeCount); for (int i = 0; i < fShapeCount; i++) { @@ -210,7 +210,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src) { SkSafeRef(s); fShapes[i] = s; } - + fRegionCount = src.fRegionCount; fRegions = SkNEW_ARRAY(SkRegion, fRegionCount); for (i = 0; i < fRegionCount; i++) { @@ -226,9 +226,9 @@ void SkPicturePlayback::init() { fPictureRefs = NULL; fShapes = NULL; fRegions = NULL; - fBitmapCount = fMatrixCount = fPaintCount = fPictureCount = + fBitmapCount = fMatrixCount = fPaintCount = fPictureCount = fRegionCount = fShapeCount = 0; - + fFactoryPlayback = NULL; } @@ -239,19 +239,19 @@ SkPicturePlayback::~SkPicturePlayback() { SkDELETE_ARRAY(fMatrices); SkDELETE_ARRAY(fPaints); SkDELETE_ARRAY(fRegions); - - fPathHeap->safeUnref(); - + + SkSafeUnref(fPathHeap); + for (int i = 0; i < fPictureCount; i++) { fPictureRefs[i]->unref(); } SkDELETE_ARRAY(fPictureRefs); - + for (int i = 0; i < fShapeCount; i++) { SkSafeUnref(fShapes[i]); } SkDELETE_ARRAY(fShapes); - + SkDELETE(fFactoryPlayback); } @@ -299,13 +299,13 @@ static void writeTagSize(SkWStream* stream, uint32_t tag, static void writeFactories(SkWStream* stream, const SkFactoryRecorder& rec) { int count = rec.count(); - + writeTagSize(stream, PICT_FACTORY_TAG, count); - + SkAutoSTMalloc<16, SkFlattenable::Factory> storage(count); SkFlattenable::Factory* array = (SkFlattenable::Factory*)storage.get(); rec.get(array); - + for (int i = 0; i < count; i++) { const char* name = SkFlattenable::FactoryToName(array[i]); // SkDebugf("---- write factories [%d] %p <%s>\n", i, array[i], name); @@ -321,13 +321,13 @@ static void writeFactories(SkWStream* stream, const SkFactoryRecorder& rec) { static void writeTypefaces(SkWStream* stream, const SkRefCntRecorder& rec) { int count = rec.count(); - + writeTagSize(stream, PICT_TYPEFACE_TAG, count); - + SkAutoSTMalloc<16, SkTypeface*> storage(count); SkTypeface** array = (SkTypeface**)storage.get(); rec.get((SkRefCnt**)array); - + for (int i = 0; i < count; i++) { array[i]->serialize(stream); } @@ -336,7 +336,7 @@ static void writeTypefaces(SkWStream* stream, const SkRefCntRecorder& rec) { void SkPicturePlayback::serialize(SkWStream* stream) const { writeTagSize(stream, PICT_READER_TAG, fReader.size()); stream->write(fReader.base(), fReader.size()); - + SkRefCntRecorder typefaceRecorder; SkFactoryRecorder factRecorder; @@ -347,7 +347,7 @@ void SkPicturePlayback::serialize(SkWStream* stream) const { buffer.setFactoryRecorder(&factRecorder); int i; - + writeTagSize(buffer, PICT_BITMAP_TAG, fBitmapCount); for (i = 0; i < fBitmapCount; i++) { fBitmaps[i].flatten(buffer); @@ -360,7 +360,7 @@ void SkPicturePlayback::serialize(SkWStream* stream) const { for (i = 0; i < fPaintCount; i++) { fPaints[i].flatten(buffer); } - + { int count = fPathHeap ? fPathHeap->count() : 0; writeTagSize(buffer, PICT_PATH_TAG, count); @@ -368,7 +368,7 @@ void SkPicturePlayback::serialize(SkWStream* stream) const { fPathHeap->flatten(buffer); } } - + writeTagSize(buffer, PICT_REGION_TAG, fRegionCount); for (i = 0; i < fRegionCount; i++) { uint32_t size = fRegions[i].flatten(NULL); @@ -377,7 +377,7 @@ void SkPicturePlayback::serialize(SkWStream* stream) const { fRegions[i].flatten(storage.get()); buffer.writePad(storage.get(), size); } - + writeTagSize(buffer, PICT_SHAPE_TAG, fShapeCount); for (i = 0; i < fShapeCount; i++) { buffer.writeFlattenable(fShapes[i]); @@ -392,7 +392,7 @@ void SkPicturePlayback::serialize(SkWStream* stream) const { for (i = 0; i < fPictureCount; i++) { fPictureRefs[i]->serialize(stream); } - + writeTagSize(stream, PICT_ARRAYS_TAG, buffer.size()); buffer.writeToStream(stream); } @@ -419,14 +419,14 @@ SkPicturePlayback::SkPicturePlayback(SkStream* stream) { this->init(); int i; - + { size_t size = readTagSize(stream, PICT_READER_TAG); void* storage = sk_malloc_throw(size); stream->read(storage, size); fReader.setMemory(storage, size); } - + int factoryCount = readTagSize(stream, PICT_FACTORY_TAG); fFactoryPlayback = SkNEW_ARGS(SkFactoryPlayback, (factoryCount)); for (i = 0; i < factoryCount; i++) { @@ -437,19 +437,19 @@ SkPicturePlayback::SkPicturePlayback(SkStream* stream) { // SkDebugf("--- factory playback [%d] <%s>\n", i, str.c_str()); fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str()); } - + int typefaceCount = readTagSize(stream, PICT_TYPEFACE_TAG); fTFPlayback.setCount(typefaceCount); for (i = 0; i < typefaceCount; i++) { fTFPlayback.set(i, SkTypeface::Deserialize(stream))->unref(); - } + } fPictureCount = readTagSize(stream, PICT_PICTURE_TAG); fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount); for (i = 0; i < fPictureCount; i++) { fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream)); } - + /* Now read the arrays chunk, and parse using a read buffer */ @@ -470,20 +470,20 @@ SkPicturePlayback::SkPicturePlayback(SkStream* stream) { fMatrixCount = readTagSize(buffer, PICT_MATRIX_TAG); fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount); buffer.read(fMatrices, fMatrixCount * sizeof(SkMatrix)); - + fPaintCount = readTagSize(buffer, PICT_PAINT_TAG); fPaints = SkNEW_ARRAY(SkPaint, fPaintCount); for (i = 0; i < fPaintCount; i++) { fPaints[i].unflatten(buffer); } - + { int count = readTagSize(buffer, PICT_PATH_TAG); if (count > 0) { fPathHeap = SkNEW_ARGS(SkPathHeap, (buffer)); } } - + fRegionCount = readTagSize(buffer, PICT_REGION_TAG); fRegions = SkNEW_ARRAY(SkRegion, fRegionCount); for (i = 0; i < fRegionCount; i++) { @@ -506,12 +506,12 @@ SkPicturePlayback::SkPicturePlayback(SkStream* stream) { struct SkipClipRec { int fCount; size_t fSize; - + SkipClipRec() { fCount = 0; fSize = 0; } - + void recordSkip(size_t bytes) { fCount += 1; fSize += bytes; @@ -523,7 +523,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas) { #ifdef ENABLE_TIME_DRAW SkAutoTime at("SkPicture::draw", 50); #endif - + #ifdef SPEW_CLIP_SKIPPING SkipClipRec skipRect, skipRegion, skipPath; #endif @@ -539,7 +539,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas) { const SkPath& path = getPath(); SkRegion::Op op = (SkRegion::Op) getInt(); size_t offsetToRestore = getInt(); - // HACK (false) until I can handle op==kReplace + // HACK (false) until I can handle op==kReplace if (!canvas.clipPath(path, op)) { #ifdef SPEW_CLIP_SKIPPING skipPath.recordSkip(offsetToRestore - fReader.offset()); @@ -644,7 +644,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas) { } break; case DRAW_RECT: { const SkPaint& paint = *getPaint(); - canvas.drawRect(*fReader.skipRect(), paint); + canvas.drawRect(*fReader.skipRect(), paint); } break; case DRAW_SHAPE: { SkShape* shape = getShape(); @@ -654,10 +654,10 @@ void SkPicturePlayback::draw(SkCanvas& canvas) { } break; case DRAW_SPRITE: { const SkPaint* paint = getPaint(); - const SkBitmap& bitmap = getBitmap(); + const SkBitmap& bitmap = getBitmap(); int left = getInt(); int top = getInt(); - canvas.drawSprite(bitmap, left, top, paint); + canvas.drawSprite(bitmap, left, top, paint); } break; case DRAW_TEXT: { const SkPaint& paint = *getPaint(); @@ -685,7 +685,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas) { getText(&text); const SkPath& path = getPath(); const SkMatrix* matrix = getMatrix(); - canvas.drawTextOnPath(text.text(), text.length(), path, + canvas.drawTextOnPath(text.text(), text.length(), path, matrix, paint); } break; case DRAW_VERTICES: { @@ -751,7 +751,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas) { SkASSERT(0); } } - + #ifdef SPEW_CLIP_SKIPPING { size_t size = skipRect.fSize + skipPath.fSize + skipRegion.fSize; @@ -770,7 +770,7 @@ void SkPicturePlayback::abort() { /////////////////////////////////////////////////////////////////////////////// #if 0 -uint32_t SkPicturePlayback::flatten(void* storage) const { +uint32_t SkPicturePlayback::flatten(void* storage) const { SkWBuffer buffer(storage); buffer.write32(fBitmapCount); int index; @@ -851,7 +851,7 @@ void SkPicturePlayback::unflatten(const void* storage) { const void* local = buffer.skip(size); fPaths[index].unflatten(local); } - + #if 0 fPictureCount = buffer.readU32(); fPictures = new SkPicture[fPictureCount]; @@ -861,7 +861,7 @@ void SkPicturePlayback::unflatten(const void* storage) { fPictures[index].unflatten(local); } #endif - + fRegionCount = buffer.readU32(); fRegions = new SkRegion[fRegionCount]; for (index = 0; index < fRegionCount; index++) { @@ -878,14 +878,14 @@ void SkPicturePlayback::unflatten(const void* storage) { /////////////////////////////////////////////////////////////////////////////// #ifdef SK_DEBUG_SIZE -int SkPicturePlayback::size(size_t* sizePtr) { +int SkPicturePlayback::size(size_t* sizePtr) { int objects = bitmaps(sizePtr); objects += paints(sizePtr); objects += paths(sizePtr); objects += pictures(sizePtr); objects += regions(sizePtr); *sizePtr = fReader.size(); - return objects; + return objects; } int SkPicturePlayback::bitmaps(size_t* size) { @@ -1112,7 +1112,7 @@ int SkPicturePlayback::dumpInt(char* bufferPtr, char* buffer, char* name) { int SkPicturePlayback::dumpRect(char* bufferPtr, char* buffer, char* name) { const SkRect* rect = fReader.skipRect(); return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), - "%s:{l:%g t:%g r:%g b:%g}, ", name, SkScalarToFloat(rect.fLeft), + "%s:{l:%g t:%g r:%g b:%g}, ", name, SkScalarToFloat(rect.fLeft), SkScalarToFloat(rect.fTop), SkScalarToFloat(rect.fRight), SkScalarToFloat(rect.fBottom)); } @@ -1121,7 +1121,7 @@ int SkPicturePlayback::dumpPoint(char* bufferPtr, char* buffer, char* name) { SkPoint pt; getPoint(&pt); return snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), - "%s:{x:%g y:%g}, ", name, SkScalarToFloat(pt.fX), + "%s:{x:%g y:%g}, ", name, SkScalarToFloat(pt.fX), SkScalarToFloat(pt.fY)); } @@ -1133,7 +1133,7 @@ void SkPicturePlayback::dumpPointArray(char** bufferPtrPtr, char* buffer, int co "count:%d {", count); for (int index = 0; index < count; index++) bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), - "{x:%g y:%g}, ", SkScalarToFloat(pts[index].fX), + "{x:%g y:%g}, ", SkScalarToFloat(pts[index].fX), SkScalarToFloat(pts[index].fY)); bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - buffer), "} "); @@ -1343,10 +1343,10 @@ void SkPicturePlayback::dump() const { dumpBitmap(bitmap); } if (fBitmapCount > 0) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "Bitmaps bitmaps = {"); for (index = 0; index < fBitmapCount; index++) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "bitmap%p, ", &fBitmaps[index]); if (fBitmapCount > 0) SkDebugf("%s0};\n", pBuffer); @@ -1359,10 +1359,10 @@ void SkPicturePlayback::dump() const { } bufferPtr = pBuffer; if (fMatrixCount > 0) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "Matrices matrices = {"); for (index = 0; index < fMatrixCount; index++) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "matrix%p, ", &fMatrices[index]); if (fMatrixCount > 0) SkDebugf("%s0};\n", pBuffer); @@ -1375,10 +1375,10 @@ void SkPicturePlayback::dump() const { } bufferPtr = pBuffer; if (fPaintCount > 0) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "Paints paints = {"); for (index = 0; index < fPaintCount; index++) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "paint%p, ", &fPaints[index]); if (fPaintCount > 0) SkDebugf("%s0};\n", pBuffer); @@ -1389,10 +1389,10 @@ void SkPicturePlayback::dump() const { } bufferPtr = pBuffer; if (fPathCount > 0) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "Paths paths = {"); for (index = 0; index < fPathCount; index++) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "path%p, ", &fPaths[index]); if (fPathCount > 0) SkDebugf("%s0};\n", pBuffer); @@ -1402,10 +1402,10 @@ void SkPicturePlayback::dump() const { } bufferPtr = pBuffer; if (fPictureCount > 0) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "Pictures pictures = {"); for (index = 0; index < fPictureCount; index++) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "picture%p, ", fPictureRefs[index]); if (fPictureCount > 0) SkDebugf("%s0};\n", pBuffer); @@ -1416,10 +1416,10 @@ void SkPicturePlayback::dump() const { } bufferPtr = pBuffer; if (fRegionCount > 0) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "Regions regions = {"); for (index = 0; index < fRegionCount; index++) - bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), + bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), "region%p, ", &fRegions[index]); if (fRegionCount > 0) SkDebugf("%s0};\n", pBuffer); diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index 9844a48..00e4b60 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -15,7 +15,7 @@ SkPictureRecord::SkPictureRecord(uint32_t flags) : fRestoreOffsetStack.setReserve(32); fRestoreOffsetStack.push(0); - + fPathHeap = NULL; // lazy allocate } @@ -28,9 +28,9 @@ SkPictureRecord::~SkPictureRecord() { int SkPictureRecord::save(SaveFlags flags) { addDraw(SAVE); addInt(flags); - + fRestoreOffsetStack.push(0); - + validate(); return this->INHERITED::save(flags); } @@ -84,7 +84,7 @@ bool SkPictureRecord::translate(SkScalar dx, SkScalar dy) { } bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) { - addDraw(SCALE); + addDraw(SCALE); addScalar(sx); addScalar(sy); validate(); @@ -92,14 +92,14 @@ bool SkPictureRecord::scale(SkScalar sx, SkScalar sy) { } bool SkPictureRecord::rotate(SkScalar degrees) { - addDraw(ROTATE); - addScalar(degrees); + addDraw(ROTATE); + addScalar(degrees); validate(); return this->INHERITED::rotate(degrees); } bool SkPictureRecord::skew(SkScalar sx, SkScalar sy) { - addDraw(SKEW); + addDraw(SKEW); addScalar(sx); addScalar(sy); validate(); @@ -126,7 +126,7 @@ bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op) { addDraw(CLIP_RECT); addRect(rect); addInt(op); - + size_t offset = fWriter.size(); addInt(fRestoreOffsetStack.top()); fRestoreOffsetStack.top() = offset; @@ -139,13 +139,13 @@ bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op) { addDraw(CLIP_PATH); addPath(path); addInt(op); - + size_t offset = fWriter.size(); addInt(fRestoreOffsetStack.top()); fRestoreOffsetStack.top() = offset; - + validate(); - + if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) { return this->INHERITED::clipRect(path.getBounds(), op); } else { @@ -154,14 +154,14 @@ bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op) { } bool SkPictureRecord::clipRegion(const SkRegion& region, SkRegion::Op op) { - addDraw(CLIP_REGION); + addDraw(CLIP_REGION); addRegion(region); addInt(op); - + size_t offset = fWriter.size(); addInt(fRestoreOffsetStack.top()); fRestoreOffsetStack.top() = offset; - + validate(); return this->INHERITED::clipRegion(region, op); } @@ -250,10 +250,10 @@ void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, addScalar(bounds.fBottom); } -void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x, +void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { bool fast = paint.canComputeFastBounds(); - + addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT); addPaint(paint); addText(text, byteLength); @@ -265,7 +265,7 @@ void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x, validate(); } -void SkPictureRecord::drawPosText(const void* text, size_t byteLength, +void SkPictureRecord::drawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) { size_t points = paint.countText(text, byteLength); if (0 == points) @@ -282,7 +282,7 @@ void SkPictureRecord::drawPosText(const void* text, size_t byteLength, } } } - + bool fast = canUseDrawH && paint.canComputeFastBounds(); if (fast) { @@ -303,7 +303,7 @@ void SkPictureRecord::drawPosText(const void* text, size_t byteLength, } addScalar(pos[0].fY); SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar)); - for (size_t index = 0; index < points; index++) + for (size_t index = 0; index < points; index++) *xptr++ = pos[index].fX; } else { @@ -322,14 +322,14 @@ void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength, size_t points = paint.countText(text, byteLength); if (0 == points) return; - + bool fast = paint.canComputeFastBounds(); addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H); addPaint(paint); addText(text, byteLength); addInt(points); - + #ifdef SK_DEBUG_SIZE size_t start = fWriter.size(); #endif @@ -345,8 +345,8 @@ void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength, validate(); } -void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength, - const SkPath& path, const SkMatrix* matrix, +void SkPictureRecord::drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) { addDraw(DRAW_TEXT_ON_PATH); addPaint(paint); @@ -417,9 +417,9 @@ void SkPictureRecord::drawData(const void* data, size_t length) { } /////////////////////////////////////////////////////////////////////////////// - + void SkPictureRecord::reset() { - fPathHeap->safeUnref(); + SkSafeUnref(fPathHeap); fPathHeap = NULL; fBitmaps.reset(); @@ -430,10 +430,10 @@ void SkPictureRecord::reset() { fShapes.safeUnrefAll(); fWriter.reset(); fHeap.reset(); - + fRestoreOffsetStack.setCount(1); fRestoreOffsetStack.top() = 0; - + fRCRecorder.reset(); fTFRecorder.reset(); } @@ -486,7 +486,7 @@ void SkPictureRecord::addPoint(const SkPoint& point) { fPointWrites++; #endif } - + void SkPictureRecord::addPoints(const SkPoint pts[], int count) { fWriter.writeMul4(pts, count * sizeof(SkPoint)); #ifdef SK_DEBUG_SIZE @@ -539,7 +539,7 @@ void SkPictureRecord::addText(const void* text, size_t byteLength) { int SkPictureRecord::find(SkTDArray<const SkFlatBitmap* >& bitmaps, const SkBitmap& bitmap) { SkFlatBitmap* flat = SkFlatBitmap::Flatten(&fHeap, bitmap, fBitmapIndex, &fRCRecorder); - int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(), + int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(), bitmaps.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); if (index >= 0) { (void)fHeap.unalloc(flat); @@ -554,7 +554,7 @@ int SkPictureRecord::find(SkTDArray<const SkFlatMatrix* >& matrices, const SkMat if (matrix == NULL) return 0; SkFlatMatrix* flat = SkFlatMatrix::Flatten(&fHeap, *matrix, fMatrixIndex); - int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(), + int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(), matrices.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); if (index >= 0) { (void)fHeap.unalloc(flat); @@ -569,10 +569,10 @@ int SkPictureRecord::find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* if (paint == NULL) { return 0; } - + SkFlatPaint* flat = SkFlatPaint::Flatten(&fHeap, *paint, fPaintIndex, &fRCRecorder, &fTFRecorder); - int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(), + int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(), paints.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); if (index >= 0) { (void)fHeap.unalloc(flat); @@ -586,7 +586,7 @@ int SkPictureRecord::find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* int SkPictureRecord::find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region) { SkFlatRegion* flat = SkFlatRegion::Flatten(&fHeap, region, fRegionIndex); - int index = SkTSearch<SkFlatData>((const SkFlatData**) regions.begin(), + int index = SkTSearch<SkFlatData>((const SkFlatData**) regions.begin(), regions.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare); if (index >= 0) { (void)fHeap.unalloc(flat); @@ -610,7 +610,7 @@ void SkPictureRecord::dumpMatrices() { void SkPictureRecord::dumpPaints() { int count = fPaints.count(); - for (int index = 0; index < count; index++) + for (int index = 0; index < count; index++) fPaints[index]->dump(); } #endif @@ -638,7 +638,7 @@ size_t SkPictureRecord::size() const { int SkPictureRecord::bitmaps(size_t* size) const { size_t result = 0; int count = fBitmaps.count(); - for (int index = 0; index < count; index++) + for (int index = 0; index < count; index++) result += sizeof(fBitmaps[index]) + fBitmaps[index]->size(); *size = result; return count; @@ -653,7 +653,7 @@ int SkPictureRecord::matrices(size_t* size) const { int SkPictureRecord::paints(size_t* size) const { size_t result = 0; int count = fPaints.count(); - for (int index = 0; index < count; index++) + for (int index = 0; index < count; index++) result += sizeof(fPaints[index]) + fPaints[index]->size(); *size = result; return count; @@ -662,7 +662,7 @@ int SkPictureRecord::paints(size_t* size) const { int SkPictureRecord::paths(size_t* size) const { size_t result = 0; int count = fPaths.count(); - for (int index = 0; index < count; index++) + for (int index = 0; index < count; index++) result += sizeof(fPaths[index]) + fPaths[index]->size(); *size = result; return count; @@ -671,7 +671,7 @@ int SkPictureRecord::paths(size_t* size) const { int SkPictureRecord::regions(size_t* size) const { size_t result = 0; int count = fRegions.count(); - for (int index = 0; index < count; index++) + for (int index = 0; index < count; index++) result += sizeof(fRegions[index]) + fRegions[index]->size(); *size = result; return count; @@ -708,7 +708,7 @@ void SkPictureRecord::validateMatrices() const { for (int index = 0; index < count; index++) { const SkFlatMatrix* matrix = fMatrices[index]; SkASSERT(matrix); - matrix->validate(); + matrix->validate(); } } diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp index 8bacfae..c376412 100644 --- a/src/core/SkPixelRef.cpp +++ b/src/core/SkPixelRef.cpp @@ -129,9 +129,9 @@ const char* SkPixelRef::FactoryToName(Factory fact) { } void SkPixelRef::globalRef(void* data) { - ref(); + this->ref(); } void SkPixelRef::globalUnref() { - unref(); + this->unref(); } diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp index 7d73249..2c6d188 100644 --- a/src/core/SkRect.cpp +++ b/src/core/SkRect.cpp @@ -16,17 +16,16 @@ #include "SkRect.h" -void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom) -{ +void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom) { // do nothing if the params are empty - if (left >= right || top >= bottom) + if (left >= right || top >= bottom) { return; + } // if we are empty, just assign - if (fLeft >= fRight || fTop >= fBottom) + if (fLeft >= fRight || fTop >= fBottom) { this->set(left, top, right, bottom); - else - { + } else { if (left < fLeft) fLeft = left; if (top < fTop) fTop = top; if (right > fRight) fRight = right; @@ -34,26 +33,34 @@ void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom) } } -void SkIRect::sort() -{ - if (fLeft > fRight) +void SkIRect::sort() { + if (fLeft > fRight) { SkTSwap<int32_t>(fLeft, fRight); - if (fTop > fBottom) + } + if (fTop > fBottom) { SkTSwap<int32_t>(fTop, fBottom); + } } ///////////////////////////////////////////////////////////////////////////// -void SkRect::sort() -{ - if (fLeft > fRight) +bool SkRect::hasValidCoordinates() const { + return SkScalarIsFinite(fLeft) && + SkScalarIsFinite(fTop) && + SkScalarIsFinite(fRight) && + SkScalarIsFinite(fBottom); +} + +void SkRect::sort() { + if (fLeft > fRight) { SkTSwap<SkScalar>(fLeft, fRight); - if (fTop > fBottom) + } + if (fTop > fBottom) { SkTSwap<SkScalar>(fTop, fBottom); + } } -void SkRect::toQuad(SkPoint quad[4]) const -{ +void SkRect::toQuad(SkPoint quad[4]) const { SkASSERT(quad); quad[0].set(fLeft, fTop); @@ -62,8 +69,7 @@ void SkRect::toQuad(SkPoint quad[4]) const quad[3].set(fLeft, fBottom); } -void SkRect::set(const SkPoint pts[], int count) -{ +void SkRect::set(const SkPoint pts[], int count) { SkASSERT((pts && count > 0) || count == 0); if (count <= 0) { @@ -71,14 +77,14 @@ void SkRect::set(const SkPoint pts[], int count) } else { #ifdef SK_SCALAR_SLOW_COMPARES int32_t l, t, r, b; - + l = r = SkScalarAs2sCompliment(pts[0].fX); t = b = SkScalarAs2sCompliment(pts[0].fY); - + for (int i = 1; i < count; i++) { int32_t x = SkScalarAs2sCompliment(pts[i].fX); int32_t y = SkScalarAs2sCompliment(pts[i].fY); - + if (x < l) l = x; else if (x > r) r = x; if (y < t) t = y; else if (y > b) b = y; } @@ -104,8 +110,8 @@ void SkRect::set(const SkPoint pts[], int count) } } -bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) -{ +bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, + SkScalar bottom) { if (left < right && top < bottom && !this->isEmpty() && // check for empties fLeft < right && left < fRight && fTop < bottom && top < fBottom) { @@ -118,23 +124,22 @@ bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bot return false; } -bool SkRect::intersect(const SkRect& r) -{ +bool SkRect::intersect(const SkRect& r) { SkASSERT(&r); return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom); } -void SkRect::join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) -{ +void SkRect::join(SkScalar left, SkScalar top, SkScalar right, + SkScalar bottom) { // do nothing if the params are empty - if (left >= right || top >= bottom) + if (left >= right || top >= bottom) { return; - + } + // if we are empty, just assign - if (fLeft >= fRight || fTop >= fBottom) + if (fLeft >= fRight || fTop >= fBottom) { this->set(left, top, right, bottom); - else - { + } else { if (left < fLeft) fLeft = left; if (top < fTop) fTop = top; if (right > fRight) fRight = right; @@ -142,4 +147,3 @@ void SkRect::join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) } } - diff --git a/src/core/SkRefDict.cpp b/src/core/SkRefDict.cpp new file mode 100644 index 0000000..6f1e7ca --- /dev/null +++ b/src/core/SkRefDict.cpp @@ -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. + */ + +#include "SkRefDict.h" +#include "SkString.h" + +struct SkRefDict::Impl { + Impl* fNext; + SkString fName; + SkRefCnt* fData; +}; + +SkRefDict::SkRefDict() : fImpl(NULL) {} + +SkRefDict::~SkRefDict() { + this->removeAll(); +} + +SkRefCnt* SkRefDict::find(const char name[]) const { + if (NULL == name) { + return NULL; + } + + Impl* rec = fImpl; + while (rec) { + if (rec->fName.equals(name)) { + return rec->fData; + } + rec = rec->fNext; + } + return NULL; +} + +void SkRefDict::set(const char name[], SkRefCnt* data) { + if (NULL == name) { + return; + } + + Impl* rec = fImpl; + Impl* prev = NULL; + while (rec) { + if (rec->fName.equals(name)) { + if (data) { + // replace + data->ref(); + rec->fData->unref(); + rec->fData = data; + } else { + // remove + rec->fData->unref(); + if (prev) { + prev->fNext = rec->fNext; + } else { + fImpl = rec->fNext; + } + } + return; + } + prev = rec; + rec = rec->fNext; + } + + // if get here, name was not found, so add it + data->ref(); + rec = new Impl; + rec->fName.set(name); + rec->fData = data; + // prepend to the head of our list + rec->fNext = fImpl; + fImpl = rec; +} + +void SkRefDict::removeAll() { + Impl* rec = fImpl; + while (rec) { + Impl* next = rec->fNext; + rec->fData->unref(); + delete rec; + rec = next; + } + fImpl = NULL; +} + diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp index 565b6ad..dfb3aa0 100644 --- a/src/core/SkRegion.cpp +++ b/src/core/SkRegion.cpp @@ -36,27 +36,6 @@ static SkRegion::RunType* skip_scanline(const SkRegion::RunType runs[]) return (SkRegion::RunType*)(runs + 1); // return past the X-sentinel } -static SkRegion::RunType* find_y(const SkRegion::RunType runs[], int y) -{ - int top = *runs++; - if (top <= y) - { - for (;;) - { - int bot = *runs++; - if (bot > y) - { - if (bot == SkRegion::kRunTypeSentinel || *runs == SkRegion::kRunTypeSentinel) - break; - return (SkRegion::RunType*)runs; - } - top = bot; - runs = skip_scanline(runs); - } - } - return NULL; -} - // returns true if runs are just a rect bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count, SkIRect* bounds) { @@ -581,7 +560,21 @@ void SkRegion::translate(int dx, int dy, SkRegion* dst) const SkDEBUGCODE(this->validate();) } -///////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool SkRegion::setRects(const SkIRect rects[], int count) { + if (0 == count) { + this->setEmpty(); + } else { + this->setRect(rects[0]); + for (int i = 1; i < count; i++) { + this->op(rects[i], kUnion_Op); + } + } + return !this->isEmpty(); +} + +/////////////////////////////////////////////////////////////////////////////// #if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized #pragma warning ( push ) @@ -1278,40 +1271,57 @@ void SkRegion::Cliperator::next() { } } -////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left, int right) -{ +static SkRegion::RunType* find_y(const SkRegion::RunType runs[], int y) { + int top = *runs++; + if (top <= y) { + for (;;) { + int bot = *runs++; + if (bot > y) { + if (bot == SkRegion::kRunTypeSentinel || + *runs == SkRegion::kRunTypeSentinel) { + break; + } + return (SkRegion::RunType*)runs; + } + runs = skip_scanline(runs); + } + } + return NULL; +} + +SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left, + int right) { SkDEBUGCODE(rgn.validate();) const SkIRect& r = rgn.getBounds(); fDone = true; - if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom && right > r.fLeft && left < r.fRight) - { - if (rgn.isRect()) - { - if (left < r.fLeft) + if (!rgn.isEmpty() && y >= r.fTop && y < r.fBottom && + right > r.fLeft && left < r.fRight) { + if (rgn.isRect()) { + if (left < r.fLeft) { left = r.fLeft; - if (right > r.fRight) + } + if (right > r.fRight) { right = r.fRight; - + } fLeft = left; fRight = right; fRuns = NULL; // means we're a rect, not a rgn fDone = false; - } - else - { - const SkRegion::RunType* runs = find_y(rgn.fRunHead->readonly_runs(), y); - if (runs) - { - for (;;) - { - if (runs[0] >= right) // runs[0..1] is to the right of the span, so we're done + } else { + const SkRegion::RunType* runs = find_y( + rgn.fRunHead->readonly_runs(), y); + if (runs) { + for (;;) { + // runs[0..1] is to the right of the span, so we're done + if (runs[0] >= right) { break; - if (runs[1] <= left) // runs[0..1] is to the left of the span, so continue - { + } + // runs[0..1] is to the left of the span, so continue + if (runs[1] <= left) { runs += 2; continue; } @@ -1327,32 +1337,37 @@ SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left, int right } } -bool SkRegion::Spanerator::next(int* left, int* right) -{ - if (fDone) return false; +bool SkRegion::Spanerator::next(int* left, int* right) { + if (fDone) { + return false; + } - if (fRuns == NULL) // we're a rect - { + if (fRuns == NULL) { // we're a rect fDone = true; // ok, now we're done - if (left) *left = fLeft; - if (right) *right = fRight; + if (left) { + *left = fLeft; + } + if (right) { + *right = fRight; + } return true; // this interval is legal } const SkRegion::RunType* runs = fRuns; - if (runs[0] >= fRight) - { + if (runs[0] >= fRight) { fDone = true; return false; } SkASSERT(runs[1] > fLeft); - if (left) + if (left) { *left = SkMax32(fLeft, runs[0]); - if (right) + } + if (right) { *right = SkMin32(fRight, runs[1]); + } fRuns = runs + 2; return true; } diff --git a/src/core/SkRegion_rects.cpp b/src/core/SkRegion_rects.cpp new file mode 100644 index 0000000..0e77b1a --- /dev/null +++ b/src/core/SkRegion_rects.cpp @@ -0,0 +1,283 @@ +#include "SkRegion.h" +#include "SkChunkAlloc.h" +#include "SkTDArray.h" +#include "SkTemplates.h" + +#if 0 + +struct VEdge { + VEdge* fPrev; + VEdge* fNext; + + SkRegion::RunType fX; + SkRegion::RunType fTop; + SkRegion::RunType fBottom; + int fWinding; + + void removeFromList() { + fPrev->fNext = fNext; + fNext->fPrev = fPrev; + } + + void backwardsInsert() { + while (fPrev->fX > fX) { + VEdge* prev = fPrev; + VEdge* next = this; + + // remove prev from the list + prev->fPrev->fNext = next; + next->fPrev = prev->fPrev; + + // insert prev after next + prev->fNext = next->fNext; + next->fNext->fPrev = prev; + next->fNext = prev; + prev->fPrev = next; + } + } + + static void SetFromRect(VEdge edges[], const SkIRect& r) { + edges[0].fX = r.fLeft; + edges[0].fTop = r.fTop; + edges[0].fBottom = r.fBottom; + edges[0].fWinding = -1; + + edges[1].fX = r.fRight; + edges[1].fTop = r.fTop; + edges[1].fBottom = r.fBottom; + edges[1].fWinding = 1; + } +}; + +class Accumulator { +public: + Accumulator(SkRegion::RunType top, int numRects); + ~Accumulator() {} + + SkRegion::RunType append(SkRegion::RunType top, const VEdge* edge); + + int count() const { return fTotalCount; } + void copyTo(SkRegion::RunType dst[]); + +private: + struct Row { + SkRegion::RunType* fPtr; + SkRegion::RunType fBottom; + int fCount; // just [L R] count + }; + SkChunkAlloc fAlloc; + SkTDArray<Row> fRows; + SkRegion::RunType fTop; + int fTotalCount; + int fRectCount; +}; + +Accumulator::Accumulator(SkRegion::RunType top, int numRects) + : fAlloc((1 + numRects * 2 + 1) * sizeof(int32_t)) { + fRectCount = numRects; + fTop = top; + fTotalCount = 2; // Top + final sentinel +} + +//#define TRACE_ROW(code) code +#define TRACE_ROW(code) + +SkRegion::RunType Accumulator::append(SkRegion::RunType currY, const VEdge* edge) { + // worst-case size + size_t size = fRectCount * 2 * sizeof(SkRegion::RunType); + SkRegion::RunType* row = (SkRegion::RunType*)fAlloc.allocThrow(size); + SkRegion::RunType* rowHead = row; + + SkRegion::RunType nextY = SkRegion::kRunTypeSentinel; + int winding = edge->fWinding; + + // record the L R values for this row + + if (edge->fTop > currY) { + nextY = SkMin32(nextY, edge->fTop); + TRACE_ROW(SkDebugf("Y %d\n", currY);) + } else { + SkRegion::RunType currR; + *row++ = edge->fX; + TRACE_ROW(SkDebugf("Y %d [%d", currY, edge->fX);) + edge = edge->fNext; + for (;;) { + if (edge->fTop > currY) { + nextY = SkMin32(nextY, edge->fTop); + break; + } + + int prevWinding = winding; + winding += edge->fWinding; + if (0 == winding) { // we finished an interval + currR = edge->fX; + } else if (0 == prevWinding && edge->fX > currR) { + *row++ = currR; + *row++ = edge->fX; + TRACE_ROW(SkDebugf(" %d] [%d", currR, edge->fX);) + } + + nextY = SkMin32(nextY, edge->fBottom); + edge = edge->fNext; + } + SkASSERT(0 == winding); + *row++ = currR; + TRACE_ROW(SkDebugf(" %d]\n", currR);) + } + int rowCount = row - rowHead; + + // now see if we have already seen this row, or if its unique + + Row* r = fRows.count() ? &fRows[fRows.count() - 1] : NULL; + if (r && (r->fCount == rowCount) && + !memcmp(r->fPtr, rowHead, + rowCount * sizeof(SkRegion::RunType))) { + r->fBottom = nextY; // update bottom + fAlloc.unalloc(rowHead); + } else { + Row* r = fRows.append(); + r->fPtr = rowHead; + r->fBottom = nextY; + r->fCount = rowCount; + fTotalCount += 1 + rowCount + 1; + } + + return nextY; +} + +void Accumulator::copyTo(SkRegion::RunType dst[]) { + SkDEBUGCODE(SkRegion::RunType* startDst = dst;) + + *dst++ = fTop; + + const Row* curr = fRows.begin(); + const Row* stop = fRows.end(); + while (curr < stop) { + *dst++ = curr->fBottom; + memcpy(dst, curr->fPtr, curr->fCount * sizeof(SkRegion::RunType)); + dst += curr->fCount; + *dst++ = SkRegion::kRunTypeSentinel; + curr += 1; + } + *dst++ = SkRegion::kRunTypeSentinel; + SkASSERT(dst - startDst == fTotalCount); +} + +/////////////////////////////////////////////////////////////////////////////// + +template <typename T> int SkTCmp2Int(const T& a, const T& b) { + return (a < b) ? -1 : ((b < a) ? 1 : 0); +} + +static inline int SkCmp32(int32_t a, int32_t b) { + return (a < b) ? -1 : ((b < a) ? 1 : 0); +} + +static int compare_edgeptr(const void* p0, const void* p1) { + const VEdge* e0 = *static_cast<VEdge*const*>(p0); + const VEdge* e1 = *static_cast<VEdge*const*>(p1); + + SkRegion::RunType v0 = e0->fTop; + SkRegion::RunType v1 = e1->fTop; + + if (v0 == v1) { + v0 = e0->fX; + v1 = e1->fX; + } + return SkCmp32(v0, v1); +} + +// fillout edge[] from rects[], sorted. Return the head, and set the tail +// +static VEdge* sort_edges(VEdge** edgePtr, VEdge edge[], const SkIRect rects[], + int rectCount, VEdge** edgeTail) { + int i; + VEdge** ptr = edgePtr; + for (int i = 0; i < rectCount; i++) { + if (!rects[i].isEmpty()) { + VEdge::SetFromRect(edge, rects[i]); + *ptr++ = edge++; + *ptr++ = edge++; + } + } + + int edgeCount = ptr - edgePtr; + if (0 == edgeCount) { + // all the rects[] were empty + return NULL; + } + + qsort(edgePtr, edgeCount, sizeof(*edgePtr), compare_edgeptr); + for (i = 1; i < edgeCount; i++) { + edgePtr[i - 1]->fNext = edgePtr[i]; + edgePtr[i]->fPrev = edgePtr[i - 1]; + } + *edgeTail = edgePtr[edgeCount - 1]; + return edgePtr[0]; +} + +bool SkRegion::setRects(const SkIRect rects[], int rectCount) { + if (0 == rectCount) { + return this->setEmpty(); + } + if (1 == rectCount) { + return this->setRect(rects[0]); + } + + int edgeCount = rectCount * 2; + SkAutoMalloc memory((sizeof(VEdge) + sizeof(VEdge*)) * edgeCount); + VEdge** edgePtr = (VEdge**)memory.get(); + VEdge* tail, *head = (VEdge*)(edgePtr + edgeCount); + head = sort_edges(edgePtr, head, rects, rectCount, &tail); + // check if we have no edges + if (NULL == head) { + return this->setEmpty(); + } + + // at this stage, we don't really care about edgeCount, or if rectCount is + // larger that it should be (since sort_edges might have skipped some + // empty rects[]). rectCount now is just used for worst-case allocations + + VEdge headEdge, tailEdge; + headEdge.fPrev = NULL; + headEdge.fNext = head; + headEdge.fTop = SK_MinS32; + headEdge.fX = SK_MinS32; + head->fPrev = &headEdge; + + tailEdge.fPrev = tail; + tailEdge.fNext = NULL; + tailEdge.fTop = SK_MaxS32; + tail->fNext = &tailEdge; + + int32_t currY = head->fTop; + Accumulator accum(currY, rectCount); + + while (head->fNext) { + VEdge* edge = head; + // accumulate the current + SkRegion::RunType nextY = accum.append(currY, edge); + // remove the old + while (edge->fTop <= currY) { + VEdge* next = edge->fNext; + if (edge->fBottom <= nextY) { + edge->removeFromList(); + } + edge = next; + } + // insert (sorted) the new + while (edge->fTop == nextY) { + VEdge* next = edge->fNext; + edge->backwardsInsert(); + edge = next; + } + currY = nextY; + head = headEdge.fNext; + } + + SkAutoTArray<RunType> runs(accum.count()); + accum.copyTo(runs.get()); + return this->setRuns(runs.get(), accum.count()); +} + +#endif diff --git a/src/core/SkScalar.cpp b/src/core/SkScalar.cpp new file mode 100644 index 0000000..c6755d1 --- /dev/null +++ b/src/core/SkScalar.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 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 "SkScalar.h" + +SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[], + const SkScalar values[], int length) { + SkASSERT(length > 0); + SkASSERT(keys != NULL); + SkASSERT(values != NULL); +#ifdef SK_DEBUG + for (int i = 1; i < length; i++) + SkASSERT(keys[i] >= keys[i-1]); +#endif + int right = 0; + while (right < length && searchKey > keys[right]) + right++; + // Could use sentinel values to eliminate conditionals, but since the + // tables are taken as input, a simpler format is better. + if (length == right) + return values[length-1]; + if (0 == right) + return values[0]; + // Otherwise, interpolate between right - 1 and right. + SkScalar rightKey = keys[right]; + SkScalar leftKey = keys[right-1]; + SkScalar fract = SkScalarDiv(searchKey-leftKey,rightKey-leftKey); + return SkScalarInterp(values[right-1], values[right], fract); +} diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index b149be9..f98969c 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -173,9 +173,9 @@ SkScalerContext::SkScalerContext(const SkDescriptor* desc) SkScalerContext::~SkScalerContext() { SkDELETE(fNextContext); - fPathEffect->safeUnref(); - fMaskFilter->safeUnref(); - fRasterizer->safeUnref(); + SkSafeUnref(fPathEffect); + SkSafeUnref(fMaskFilter); + SkSafeUnref(fRasterizer); } static SkScalerContext* allocNextContext(const SkScalerContext::Rec& rec) { @@ -309,7 +309,7 @@ void SkScalerContext::getMetrics(SkGlyph* glyph) { glyph->fMaskFormat = 0; return; } - + if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { SkPath devPath, fillPath; SkMatrix fillToDevMatrix; @@ -333,7 +333,7 @@ void SkScalerContext::getMetrics(SkGlyph* glyph) { // just use devPath SkIRect ir; devPath.getBounds().roundOut(&ir); - + if (ir.isEmpty() || !ir.is16Bit()) { goto SK_ERROR; } @@ -344,7 +344,9 @@ void SkScalerContext::getMetrics(SkGlyph* glyph) { } } - glyph->fMaskFormat = fRec.fMaskFormat; + if (SkMask::kARGB32_Format != glyph->fMaskFormat) { + glyph->fMaskFormat = fRec.fMaskFormat; + } if (fMaskFilter) { SkMask src, dst; @@ -364,7 +366,7 @@ void SkScalerContext::getMetrics(SkGlyph* glyph) { } } return; - + SK_ERROR: // draw nothing 'cause we failed glyph->fLeft = 0; @@ -408,11 +410,11 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { if (fRasterizer) { SkMask mask; - + glyph->toMask(&mask); mask.fFormat = SkMask::kA8_Format; sk_bzero(glyph->fImage, mask.computeImageSize()); - + if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, fMaskFilter, &mask, SkMask::kJustRenderImage_CreateMode)) { @@ -470,7 +472,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height()); int dstRB = origGlyph.rowBytes(); int srcRB = dstM.fRowBytes; - + const uint8_t* src = (const uint8_t*)dstM.fImage; uint8_t* dst = (uint8_t*)origGlyph.fImage; @@ -490,7 +492,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { SkMask::FreeImage(dstM.fImage); } } - + // check to see if we should filter the alpha channel if (NULL == fMaskFilter && @@ -502,7 +504,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { { uint8_t* dst = (uint8_t*)origGlyph.fImage; unsigned rowBytes = origGlyph.rowBytes(); - + for (int y = origGlyph.fHeight - 1; y >= 0; --y) { for (int x = origGlyph.fWidth - 1; x >= 0; --x) @@ -534,7 +536,7 @@ void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, Sk SkPath path; this->getGlyphContext(glyph)->generatePath(glyph, &path); - + if (fRec.fFrameWidth > 0 || fPathEffect != NULL) { // need the path in user-space, with only the point-size applied @@ -571,14 +573,14 @@ void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, Sk stroker.strokePath(localPath, &outline); localPath.swap(outline); } - + // now return stuff to the caller if (fillToDevMatrix) *fillToDevMatrix = matrix; - + if (devPath) localPath.transform(matrix, devPath); - + if (fillPath) fillPath->swap(localPath); } @@ -586,7 +588,7 @@ void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, Sk { if (fillToDevMatrix) fillToDevMatrix->reset(); - + if (devPath) { if (fillPath == NULL) @@ -594,11 +596,11 @@ void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, Sk else *devPath = path; } - + if (fillPath) fillPath->swap(path); } - + if (devPath) devPath->updateBoundsCache(); if (fillPath) @@ -668,9 +670,14 @@ protected: } }; +extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc); + SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) { - SkScalerContext* c = SkFontHost::CreateScalerContext(desc); + SkScalerContext* c = NULL; //SkCreateColorScalerContext(desc); + if (NULL == c) { + c = SkFontHost::CreateScalerContext(desc); + } if (NULL == c) { c = SkNEW_ARGS(SkScalerContext_Empty, (desc)); } diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp index 21fd5c9..fb33ab0 100644 --- a/src/core/SkScan_AntiPath.cpp +++ b/src/core/SkScan_AntiPath.cpp @@ -82,6 +82,7 @@ public: void flush(); virtual void blitH(int x, int y, int width); + virtual void blitRect(int x, int y, int width, int height); private: SkAlphaRuns fRuns; @@ -189,6 +190,15 @@ void SuperBlitter::blitH(int x, int y, int width) #endif } +void SuperBlitter::blitRect(int x, int y, int width, int height) +{ + for (int i = 0; i < height; ++i) { + blitH(x, y + i, width); + } + + flush(); +} + /////////////////////////////////////////////////////////////////////////////// class MaskSuperBlitter : public BaseSuperBlitter { diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp index 38f16e6..b1ba7df 100644 --- a/src/core/SkScan_Path.cpp +++ b/src/core/SkScan_Path.cpp @@ -508,6 +508,14 @@ void sk_fill_path(const SkPath& path, const SkIRect* clipRect, SkBlitter* blitte #endif if (count < 2) { + if (path.isInverseFillType()) { + const SkIRect& clipRect = clipRgn.getBounds(); + blitter->blitRect(clipRect.fLeft << shiftEdgesUp, + clipRect.fTop << shiftEdgesUp, + clipRect.width() << shiftEdgesUp, + clipRect.height() << shiftEdgesUp); + } + return; } diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp index 34dc32e..7b3a024 100644 --- a/src/core/SkShader.cpp +++ b/src/core/SkShader.cpp @@ -17,6 +17,7 @@ #include "SkShader.h" #include "SkPaint.h" +#include "SkMallocPixelRef.h" SkShader::SkShader() : fLocalMatrix(NULL) { SkDEBUGCODE(fInSession = false;) @@ -195,8 +196,9 @@ SkShader::MatrixClass SkShader::ComputeMatrixClass(const SkMatrix& mat) { ////////////////////////////////////////////////////////////////////////////// -bool SkShader::asABitmap(SkBitmap*, SkMatrix*, TileMode*) { - return false; +SkShader::BitmapType SkShader::asABitmap(SkBitmap*, SkMatrix*, + TileMode*, SkScalar*) { + return kNone_BitmapType; } SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, @@ -209,9 +211,28 @@ SkShader* SkShader::CreateBitmapShader(const SkBitmap& src, #include "SkColorShader.h" #include "SkUtils.h" +SkColorShader::SkColorShader() { + fFlags = 0; + fInheritColor = true; + fAsABitmapPixelRef = NULL; +} + +SkColorShader::SkColorShader(SkColor c) { + fFlags = 0; + fColor = c; + fInheritColor = false; + fAsABitmapPixelRef = NULL; +} + +SkColorShader::~SkColorShader() { + SkSafeUnref(fAsABitmapPixelRef); +} + SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) { fFlags = 0; // computed in setContext - fInheritColor = b.readU8(); + fAsABitmapPixelRef = NULL; + + fInheritColor = b.readU8(); if (fInheritColor) { return; } @@ -285,3 +306,28 @@ void SkColorShader::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) { memset(alpha, SkGetPackedA32(fPMColor), count); } +// if we had a asAColor method, that would be more efficient... +SkShader::BitmapType SkColorShader::asABitmap(SkBitmap* bitmap, SkMatrix* matrix, + TileMode modes[], + SkScalar* twoPointRadialParams) { + // we cache the pixelref, since its generateID is used in the texture cache + if (NULL == fAsABitmapPixelRef) { + SkPMColor* storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor)); + *storage = fPMColor; + fAsABitmapPixelRef = new SkMallocPixelRef(storage, sizeof(SkPMColor), + NULL); + } + + if (bitmap) { + bitmap->setConfig(SkBitmap::kARGB_8888_Config, 1, 1); + bitmap->setPixelRef(fAsABitmapPixelRef); + } + if (matrix) { + matrix->reset(); + } + if (modes) { + modes[0] = modes[1] = SkShader::kRepeat_TileMode; + } + return kDefault_BitmapType; +} + diff --git a/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp index 01ee9f1..df9e2a1 100644 --- a/src/core/SkSpriteBlitter_ARGB32.cpp +++ b/src/core/SkSpriteBlitter_ARGB32.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -41,7 +41,7 @@ public: fProc32 = SkBlitRow::Factory32(flags32); fAlpha = alpha; } - + virtual void blitRect(int x, int y, int width, int height) { SkASSERT(width > 0 && height > 0); SK_RESTRICT uint32_t* dst = fDevice->getAddr32(x, y); @@ -62,7 +62,7 @@ public: private: SkBlitRow::Proc32 fProc32; U8CPU fAlpha; - + typedef SkSpriteBlitter INHERITED; }; @@ -73,11 +73,11 @@ public: Sprite_D32_XferFilter(const SkBitmap& source, const SkPaint& paint) : SkSpriteBlitter(source) { fColorFilter = paint.getColorFilter(); - fColorFilter->safeRef(); - + SkSafeRef(fColorFilter); + fXfermode = paint.getXfermode(); - fXfermode->safeRef(); - + SkSafeRef(fXfermode); + fBufferSize = 0; fBuffer = NULL; @@ -88,21 +88,21 @@ public: if (!source.isOpaque()) { flags32 |= SkBlitRow::kSrcPixelAlpha_Flag32; } - + fProc32 = SkBlitRow::Factory32(flags32); fAlpha = paint.getAlpha(); } - + virtual ~Sprite_D32_XferFilter() { delete[] fBuffer; - fXfermode->safeUnref(); - fColorFilter->safeUnref(); + SkSafeUnref(fXfermode); + SkSafeUnref(fColorFilter); } - + virtual void setup(const SkBitmap& device, int left, int top, const SkPaint& paint) { this->INHERITED::setup(device, left, top, paint); - + int width = device.width(); if (width > fBufferSize) { fBufferSize = width; @@ -142,12 +142,12 @@ public: do { const SkPMColor* tmp = src; - + if (NULL != colorFilter) { colorFilter->filterSpan(src, width, fBuffer); tmp = fBuffer; } - + if (NULL != xfermode) { xfermode->xfer32(dst, tmp, width, NULL); } else { @@ -158,7 +158,7 @@ public: src = (const SK_RESTRICT uint32_t*)((const char*)src + srcRB); } while (--height != 0); } - + private: typedef Sprite_D32_XferFilter INHERITED; }; @@ -166,7 +166,7 @@ private: static void fillbuffer(SK_RESTRICT SkPMColor dst[], const SK_RESTRICT SkPMColor16 src[], int count) { SkASSERT(count > 0); - + do { *dst++ = SkPixel4444ToPixel32(*src++); } while (--count != 0); @@ -176,7 +176,7 @@ class Sprite_D32_S4444_XferFilter : public Sprite_D32_XferFilter { public: Sprite_D32_S4444_XferFilter(const SkBitmap& source, const SkPaint& paint) : Sprite_D32_XferFilter(source, paint) {} - + virtual void blitRect(int x, int y, int width, int height) { SkASSERT(width > 0 && height > 0); SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y); @@ -190,7 +190,7 @@ public: do { fillbuffer(buffer, src, width); - + if (NULL != colorFilter) { colorFilter->filterSpan(buffer, width, buffer); } @@ -199,12 +199,12 @@ public: } else { fProc32(dst, buffer, width, fAlpha); } - + dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB); src = (const SK_RESTRICT SkPMColor16*)((const char*)src + srcRB); } while (--height != 0); } - + private: typedef Sprite_D32_XferFilter INHERITED; }; @@ -223,7 +223,7 @@ static void src_row(SK_RESTRICT SkPMColor dst[], class Sprite_D32_S4444_Opaque : public SkSpriteBlitter { public: Sprite_D32_S4444_Opaque(const SkBitmap& source) : SkSpriteBlitter(source) {} - + virtual void blitRect(int x, int y, int width, int height) { SkASSERT(width > 0 && height > 0); SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y); @@ -231,7 +231,7 @@ public: y - fTop); unsigned dstRB = fDevice->rowBytes(); unsigned srcRB = fSource->rowBytes(); - + do { src_row(dst, src, width); dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB); @@ -252,7 +252,7 @@ static void srcover_row(SK_RESTRICT SkPMColor dst[], class Sprite_D32_S4444 : public SkSpriteBlitter { public: Sprite_D32_S4444(const SkBitmap& source) : SkSpriteBlitter(source) {} - + virtual void blitRect(int x, int y, int width, int height) { SkASSERT(width > 0 && height > 0); SK_RESTRICT SkPMColor* dst = fDevice->getAddr32(x, y); @@ -260,7 +260,7 @@ public: y - fTop); unsigned dstRB = fDevice->rowBytes(); unsigned srcRB = fSource->rowBytes(); - + do { srcover_row(dst, src, width); dst = (SK_RESTRICT SkPMColor*)((char*)dst + dstRB); diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp index 6157105..21ee05e 100644 --- a/src/core/SkStream.cpp +++ b/src/core/SkStream.cpp @@ -123,6 +123,13 @@ bool SkWStream::writeDecAsText(int32_t dec) return this->write(tmp.c_str(), tmp.size()); } +bool SkWStream::writeBigDecAsText(int64_t dec, int minDigits) +{ + SkString tmp; + tmp.appendS64(dec, minDigits); + return this->write(tmp.c_str(), tmp.size()); +} + bool SkWStream::writeHexAsText(uint32_t hex, int digits) { SkString tmp; @@ -737,4 +744,3 @@ bool SkDebugWStream::write(const void* buffer, size_t size) #endif return true; } - diff --git a/src/core/SkString.cpp b/src/core/SkString.cpp index d595d51..cdce160 100644 --- a/src/core/SkString.cpp +++ b/src/core/SkString.cpp @@ -79,6 +79,39 @@ char* SkStrAppendS32(char string[], int32_t dec) return string; } +char* SkStrAppendS64(char string[], int64_t dec, int minDigits) +{ + SkDEBUGCODE(char* start = string;) + + char buffer[SkStrAppendS64_MaxSize]; + char* p = buffer + sizeof(buffer); + bool neg = false; + + if (dec < 0) { + neg = true; + dec = -dec; + } + do { + *--p = SkToU8('0' + dec % 10); + dec /= 10; + minDigits--; + } while (dec != 0); + while (minDigits > 0) { + *--p = '0'; + minDigits--; + } + if (neg) + *--p = '-'; + + SkASSERT(p >= buffer); + size_t cp_len = buffer + sizeof(buffer) - p; + memcpy(string, p, cp_len); + string += cp_len; + + SkASSERT(string - start <= SkStrAppendS64_MaxSize); + return string; +} + char* SkStrAppendScalar(char string[], SkScalar value) { SkDEBUGCODE(char* start = string;) @@ -440,6 +473,13 @@ void SkString::insertS32(size_t offset, int32_t dec) this->insert(offset, buffer, stop - buffer); } +void SkString::insertS64(size_t offset, int64_t dec, int minDigits) +{ + char buffer[SkStrAppendS64_MaxSize]; + char* stop = SkStrAppendS64(buffer, dec, minDigits); + this->insert(offset, buffer, stop - buffer); +} + void SkString::insertHex(size_t offset, uint32_t hex, int minDigits) { minDigits = SkPin32(minDigits, 0, 8); @@ -580,4 +620,3 @@ SkAutoUCS2::~SkAutoUCS2() { delete[] fUCS2; } - diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp index 1fb25a6..c35400b 100644 --- a/src/core/SkStroke.cpp +++ b/src/core/SkStroke.cpp @@ -633,6 +633,10 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { if (fDoFill) { dst->addPath(src); + } else { + if (src.countPoints() == 2) { + dst->setIsConvex(true); + } } } diff --git a/src/core/SkTextFormatParams.h b/src/core/SkTextFormatParams.h new file mode 100644 index 0000000..3306270 --- /dev/null +++ b/src/core/SkTextFormatParams.h @@ -0,0 +1,48 @@ +/* + * 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 SkTextFormatParams_DEFINES +#define SkTextFormatParams_DEFINES + +#include "SkScalar.h" +#include "SkTypes.h" + +// Fraction of the text size to lower a strike through line below the baseline. +#define kStdStrikeThru_Offset (-SK_Scalar1 * 6 / 21) +// Fraction of the text size to lower a underline below the baseline. +#define kStdUnderline_Offset (SK_Scalar1 / 9) +// Fraction of the text size to use for a strike through or under-line. +#define kStdUnderline_Thickness (SK_Scalar1 / 18) + +// The fraction of text size to embolden fake bold text scales with text size. +// At 9 points or below, the stroke width is increased by text size / 24. +// At 36 points and above, it is increased by text size / 32. In between, +// it is interpolated between those values. +static const SkScalar kStdFakeBoldInterpKeys[] = { + SkIntToScalar(9), + SkIntToScalar(36) +}; +static const SkScalar kStdFakeBoldInterpValues[] = { + SK_Scalar1/24, + SK_Scalar1/32 +}; +SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kStdFakeBoldInterpKeys) == + SK_ARRAY_COUNT(kStdFakeBoldInterpValues), + mismatched_array_size); +static const int kStdFakeBoldInterpLength = + SK_ARRAY_COUNT(kStdFakeBoldInterpKeys); + +#endif //SkTextFormatParams_DEFINES diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp index 65052c1..7eeaf19 100644 --- a/src/core/SkTypeface.cpp +++ b/src/core/SkTypeface.cpp @@ -1,3 +1,19 @@ +/* + * 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. + */ + #include "SkTypeface.h" #include "SkFontHost.h" @@ -12,8 +28,9 @@ uint32_t SkTypeface::UniqueID(const SkTypeface* face) { static uint32_t gDefaultFontID; if (0 == gDefaultFontID) { - SkTypeface* defaultFace = SkFontHost::CreateTypeface(NULL, NULL, - SkTypeface::kNormal); + SkTypeface* defaultFace = + SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, + SkTypeface::kNormal); SkASSERT(defaultFace); gDefaultFontID = defaultFace->uniqueID(); defaultFace->unref(); @@ -28,11 +45,16 @@ bool SkTypeface::Equal(const SkTypeface* facea, const SkTypeface* faceb) { /////////////////////////////////////////////////////////////////////////////// SkTypeface* SkTypeface::CreateFromName(const char name[], Style style) { - return SkFontHost::CreateTypeface(NULL, name, style); + return SkFontHost::CreateTypeface(NULL, name, NULL, 0, style); +} + +SkTypeface* SkTypeface::CreateForChars(const void* data, size_t bytelength, + Style s) { + return SkFontHost::CreateTypeface(NULL, NULL, data, bytelength, s); } SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s) { - return SkFontHost::CreateTypeface(family, NULL, s); + return SkFontHost::CreateTypeface(family, NULL, NULL, 0, s); } SkTypeface* SkTypeface::CreateFromStream(SkStream* stream) { @@ -53,4 +75,7 @@ SkTypeface* SkTypeface::Deserialize(SkStream* stream) { return SkFontHost::Deserialize(stream); } - +SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics( + bool perGlyphInfo) const { + return SkFontHost::GetAdvancedTypefaceMetrics(fUniqueID, perGlyphInfo); +} diff --git a/src/core/SkUtils.cpp b/src/core/SkUtils.cpp index a88233f..9fb85c2 100644 --- a/src/core/SkUtils.cpp +++ b/src/core/SkUtils.cpp @@ -124,7 +124,7 @@ void sk_memset32_portable(uint32_t dst[], uint32_t value, int count) } } -#ifndef ANDROID +#if !defined(ANDROID) || defined(SK_BUILD_FOR_ANDROID_NDK) static void sk_memset16_stub(uint16_t dst[], uint16_t value, int count) { SkMemset16Proc proc = SkMemset16GetPlatformProc(); diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp index 7ed77b2..a5b9f13 100644 --- a/src/core/SkXfermode.cpp +++ b/src/core/SkXfermode.cpp @@ -83,290 +83,6 @@ static inline int clamp_max(int value, int max) { /////////////////////////////////////////////////////////////////////////////// -bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) { - return false; -} - -SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) { - // no-op. subclasses should override this - return dst; -} - -void SkXfermode::xfer32(SK_RESTRICT SkPMColor dst[], - const SK_RESTRICT SkPMColor src[], int count, - const SK_RESTRICT SkAlpha aa[]) { - SkASSERT(dst && src && count >= 0); - - if (NULL == aa) { - for (int i = count - 1; i >= 0; --i) { - dst[i] = this->xferColor(src[i], dst[i]); - } - } else { - for (int i = count - 1; i >= 0; --i) { - unsigned a = aa[i]; - if (0 != a) { - SkPMColor dstC = dst[i]; - SkPMColor C = this->xferColor(src[i], dstC); - if (0xFF != a) { - C = SkFourByteInterp(C, dstC, a); - } - dst[i] = C; - } - } - } -} - -void SkXfermode::xfer16(SK_RESTRICT uint16_t dst[], - const SK_RESTRICT SkPMColor src[], int count, - const SK_RESTRICT SkAlpha aa[]) { - SkASSERT(dst && src && count >= 0); - - if (NULL == aa) { - for (int i = count - 1; i >= 0; --i) { - SkPMColor dstC = SkPixel16ToPixel32(dst[i]); - dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC)); - } - } else { - for (int i = count - 1; i >= 0; --i) { - unsigned a = aa[i]; - if (0 != a) { - SkPMColor dstC = SkPixel16ToPixel32(dst[i]); - SkPMColor C = this->xferColor(src[i], dstC); - if (0xFF != a) { - C = SkFourByteInterp(C, dstC, a); - } - dst[i] = SkPixel32ToPixel16_ToU16(C); - } - } - } -} - -void SkXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[], - const SK_RESTRICT SkPMColor src[], int count, - const SK_RESTRICT SkAlpha aa[]) -{ - SkASSERT(dst && src && count >= 0); - - if (NULL == aa) { - for (int i = count - 1; i >= 0; --i) { - SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); - dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC)); - } - } else { - for (int i = count - 1; i >= 0; --i) { - unsigned a = aa[i]; - if (0 != a) { - SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); - SkPMColor C = this->xferColor(src[i], dstC); - if (0xFF != a) { - C = SkFourByteInterp(C, dstC, a); - } - dst[i] = SkPixel32ToPixel4444(C); - } - } - } -} - -void SkXfermode::xferA8(SK_RESTRICT SkAlpha dst[], - const SkPMColor src[], int count, - const SK_RESTRICT SkAlpha aa[]) -{ - SkASSERT(dst && src && count >= 0); - - if (NULL == aa) { - for (int i = count - 1; i >= 0; --i) { - SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT)); - dst[i] = SkToU8(SkGetPackedA32(res)); - } - } else { - for (int i = count - 1; i >= 0; --i) { - unsigned a = aa[i]; - if (0 != a) { - SkAlpha dstA = dst[i]; - unsigned A = SkGetPackedA32(this->xferColor(src[i], - (SkPMColor)(dstA << SK_A32_SHIFT))); - if (0xFF != a) { - A = SkAlphaBlend(A, dstA, SkAlpha255To256(a)); - } - dst[i] = SkToU8(A); - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkProcXfermode::xfer32(SK_RESTRICT SkPMColor dst[], - const SK_RESTRICT SkPMColor src[], int count, - const SK_RESTRICT SkAlpha aa[]) { - SkASSERT(dst && src && count >= 0); - - SkXfermodeProc proc = fProc; - - if (NULL != proc) { - if (NULL == aa) { - for (int i = count - 1; i >= 0; --i) { - dst[i] = proc(src[i], dst[i]); - } - } else { - for (int i = count - 1; i >= 0; --i) { - unsigned a = aa[i]; - if (0 != a) { - SkPMColor dstC = dst[i]; - SkPMColor C = proc(src[i], dstC); - if (a != 0xFF) { - C = SkFourByteInterp(C, dstC, a); - } - dst[i] = C; - } - } - } - } -} - -void SkProcXfermode::xfer16(SK_RESTRICT uint16_t dst[], - const SK_RESTRICT SkPMColor src[], int count, - const SK_RESTRICT SkAlpha aa[]) { - SkASSERT(dst && src && count >= 0); - - SkXfermodeProc proc = fProc; - - if (NULL != proc) { - if (NULL == aa) { - for (int i = count - 1; i >= 0; --i) { - SkPMColor dstC = SkPixel16ToPixel32(dst[i]); - dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC)); - } - } else { - for (int i = count - 1; i >= 0; --i) { - unsigned a = aa[i]; - if (0 != a) { - SkPMColor dstC = SkPixel16ToPixel32(dst[i]); - SkPMColor C = proc(src[i], dstC); - if (0xFF != a) { - C = SkFourByteInterp(C, dstC, a); - } - dst[i] = SkPixel32ToPixel16_ToU16(C); - } - } - } - } -} - -void SkProcXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[], - const SK_RESTRICT SkPMColor src[], int count, - const SK_RESTRICT SkAlpha aa[]) { - SkASSERT(dst && src && count >= 0); - - SkXfermodeProc proc = fProc; - - if (NULL != proc) { - if (NULL == aa) { - for (int i = count - 1; i >= 0; --i) { - SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); - dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC)); - } - } else { - for (int i = count - 1; i >= 0; --i) { - unsigned a = aa[i]; - if (0 != a) { - SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); - SkPMColor C = proc(src[i], dstC); - if (0xFF != a) { - C = SkFourByteInterp(C, dstC, a); - } - dst[i] = SkPixel32ToPixel4444(C); - } - } - } - } -} - -void SkProcXfermode::xferA8(SK_RESTRICT SkAlpha dst[], - const SK_RESTRICT SkPMColor src[], int count, - const SK_RESTRICT SkAlpha aa[]) { - SkASSERT(dst && src && count >= 0); - - SkXfermodeProc proc = fProc; - - if (NULL != proc) { - if (NULL == aa) { - for (int i = count - 1; i >= 0; --i) { - SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT); - dst[i] = SkToU8(SkGetPackedA32(res)); - } - } else { - for (int i = count - 1; i >= 0; --i) { - unsigned a = aa[i]; - if (0 != a) { - SkAlpha dstA = dst[i]; - SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT); - unsigned A = SkGetPackedA32(res); - if (0xFF != a) { - A = SkAlphaBlend(A, dstA, SkAlpha255To256(a)); - } - dst[i] = SkToU8(A); - } - } - } - } -} - -SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer) - : SkXfermode(buffer) { - fProc = (SkXfermodeProc)buffer.readFunctionPtr(); - fMode = (Mode) buffer.readInt(); -} - -void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) { - buffer.writeFunctionPtr((void*)fProc); - buffer.writeInt(fMode); -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -class SkProcCoeffXfermode : public SkProcXfermode { -public: - SkProcCoeffXfermode(SkXfermodeProc proc, Coeff sc, Coeff dc) - : INHERITED(proc), fSrcCoeff(sc), fDstCoeff(dc) { - } - - virtual bool asCoeff(Coeff* sc, Coeff* dc) { - if (sc) { - *sc = fSrcCoeff; - } - if (dc) { - *dc = fDstCoeff; - } - return true; - } - - virtual Factory getFactory() { return CreateProc; } - virtual void flatten(SkFlattenableWriteBuffer& buffer) { - this->INHERITED::flatten(buffer); - buffer.write32(fSrcCoeff); - buffer.write32(fDstCoeff); - } - -protected: - SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer) - : INHERITED(buffer) { - fSrcCoeff = (Coeff)buffer.readU32(); - fDstCoeff = (Coeff)buffer.readU32(); - } - -private: - Coeff fSrcCoeff, fDstCoeff; - - static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { - return SkNEW_ARGS(SkProcCoeffXfermode, (buffer)); } - - typedef SkProcXfermode INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////// - // kClear_Mode, //!< [0, 0] static SkPMColor clear_modeproc(SkPMColor src, SkPMColor dst) { return 0; @@ -710,6 +426,342 @@ static SkPMColor exclusion_modeproc(SkPMColor src, SkPMColor dst) { return SkPackARGB32(a, r, g, b); } +struct ProcCoeff { + SkXfermodeProc fProc; + SkXfermode::Coeff fSC; + SkXfermode::Coeff fDC; +}; + +#define CANNOT_USE_COEFF SkXfermode::Coeff(-1) + +static const ProcCoeff gProcCoeffs[] = { + { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff }, + { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff }, + { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff }, + { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff }, + { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff }, + { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff }, + { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff }, + { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff }, + { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff }, + { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff }, + { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff }, + { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff }, + + { plus_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { multiply_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { screen_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { overlay_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { darken_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { lighten_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { colordodge_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { colorburn_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { hardlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { softlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { difference_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, + { exclusion_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) { + return false; +} + +bool SkXfermode::asMode(Mode* mode) { + return IsMode(this, mode); +} + +SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) { + // no-op. subclasses should override this + return dst; +} + +void SkXfermode::xfer32(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + dst[i] = this->xferColor(src[i], dst[i]); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = dst[i]; + SkPMColor C = this->xferColor(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = C; + } + } + } +} + +void SkXfermode::xfer16(SK_RESTRICT uint16_t dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel16_ToU16(this->xferColor(src[i], dstC)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + SkPMColor C = this->xferColor(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = SkPixel32ToPixel16_ToU16(C); + } + } + } +} + +void SkXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel4444(this->xferColor(src[i], dstC)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); + SkPMColor C = this->xferColor(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = SkPixel32ToPixel4444(C); + } + } + } +} + +void SkXfermode::xferA8(SK_RESTRICT SkAlpha dst[], + const SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) +{ + SkASSERT(dst && src && count >= 0); + + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor res = this->xferColor(src[i], (dst[i] << SK_A32_SHIFT)); + dst[i] = SkToU8(SkGetPackedA32(res)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkAlpha dstA = dst[i]; + unsigned A = SkGetPackedA32(this->xferColor(src[i], + (SkPMColor)(dstA << SK_A32_SHIFT))); + if (0xFF != a) { + A = SkAlphaBlend(A, dstA, SkAlpha255To256(a)); + } + dst[i] = SkToU8(A); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkProcXfermode::xfer32(SK_RESTRICT SkPMColor dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + + if (NULL != proc) { + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + dst[i] = proc(src[i], dst[i]); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = dst[i]; + SkPMColor C = proc(src[i], dstC); + if (a != 0xFF) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = C; + } + } + } + } +} + +void SkProcXfermode::xfer16(SK_RESTRICT uint16_t dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + + if (NULL != proc) { + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel16_ToU16(proc(src[i], dstC)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = SkPixel16ToPixel32(dst[i]); + SkPMColor C = proc(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = SkPixel32ToPixel16_ToU16(C); + } + } + } + } +} + +void SkProcXfermode::xfer4444(SK_RESTRICT SkPMColor16 dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + + if (NULL != proc) { + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); + dst[i] = SkPixel32ToPixel4444(proc(src[i], dstC)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkPMColor dstC = SkPixel4444ToPixel32(dst[i]); + SkPMColor C = proc(src[i], dstC); + if (0xFF != a) { + C = SkFourByteInterp(C, dstC, a); + } + dst[i] = SkPixel32ToPixel4444(C); + } + } + } + } +} + +void SkProcXfermode::xferA8(SK_RESTRICT SkAlpha dst[], + const SK_RESTRICT SkPMColor src[], int count, + const SK_RESTRICT SkAlpha aa[]) { + SkASSERT(dst && src && count >= 0); + + SkXfermodeProc proc = fProc; + + if (NULL != proc) { + if (NULL == aa) { + for (int i = count - 1; i >= 0; --i) { + SkPMColor res = proc(src[i], dst[i] << SK_A32_SHIFT); + dst[i] = SkToU8(SkGetPackedA32(res)); + } + } else { + for (int i = count - 1; i >= 0; --i) { + unsigned a = aa[i]; + if (0 != a) { + SkAlpha dstA = dst[i]; + SkPMColor res = proc(src[i], dstA << SK_A32_SHIFT); + unsigned A = SkGetPackedA32(res); + if (0xFF != a) { + A = SkAlphaBlend(A, dstA, SkAlpha255To256(a)); + } + dst[i] = SkToU8(A); + } + } + } + } +} + +SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer) + : SkXfermode(buffer) { + fProc = (SkXfermodeProc)buffer.readFunctionPtr(); + fMode = (Mode) buffer.readInt(); +} + +void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) { + buffer.writeFunctionPtr((void*)fProc); + buffer.writeInt(fMode); +} + +bool SkProcXfermode::asMode(SkXfermode::Mode* mode) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gProcCoeffs); i++) { + if (gProcCoeffs[i].fProc == fProc) { + if (mode) { + *mode = static_cast<Mode>(i); + } + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class SkProcCoeffXfermode : public SkProcXfermode { +public: + SkProcCoeffXfermode(SkXfermodeProc proc, Coeff sc, Coeff dc) + : INHERITED(proc), fSrcCoeff(sc), fDstCoeff(dc) { + } + + virtual bool asCoeff(Coeff* sc, Coeff* dc) { + if (sc) { + *sc = fSrcCoeff; + } + if (dc) { + *dc = fDstCoeff; + } + return true; + } + + virtual Factory getFactory() { return CreateProc; } + virtual void flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + buffer.write32(fSrcCoeff); + buffer.write32(fDstCoeff); + } + +protected: + SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) { + fSrcCoeff = (Coeff)buffer.readU32(); + fDstCoeff = (Coeff)buffer.readU32(); + } + +private: + Coeff fSrcCoeff, fDstCoeff; + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkProcCoeffXfermode, (buffer)); } + + typedef SkProcXfermode INHERITED; +}; + /////////////////////////////////////////////////////////////////////////////// class SkClearXfermode : public SkProcCoeffXfermode { @@ -903,42 +955,6 @@ private: /////////////////////////////////////////////////////////////////////////////// -struct ProcCoeff { - SkXfermodeProc fProc; - SkXfermode::Coeff fSC; - SkXfermode::Coeff fDC; -}; - -#define CANNOT_USE_COEFF SkXfermode::Coeff(-1) - -static const ProcCoeff gProcCoeffs[] = { - { clear_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kZero_Coeff }, - { src_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kZero_Coeff }, - { dst_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kOne_Coeff }, - { srcover_modeproc, SkXfermode::kOne_Coeff, SkXfermode::kISA_Coeff }, - { dstover_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kOne_Coeff }, - { srcin_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kZero_Coeff }, - { dstin_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kSA_Coeff }, - { srcout_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kZero_Coeff }, - { dstout_modeproc, SkXfermode::kZero_Coeff, SkXfermode::kISA_Coeff }, - { srcatop_modeproc, SkXfermode::kDA_Coeff, SkXfermode::kISA_Coeff }, - { dstatop_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kSA_Coeff }, - { xor_modeproc, SkXfermode::kIDA_Coeff, SkXfermode::kISA_Coeff }, - - { plus_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { multiply_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { screen_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { overlay_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { darken_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { lighten_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { colordodge_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { colorburn_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { hardlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { softlight_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { difference_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, - { exclusion_modeproc, CANNOT_USE_COEFF, CANNOT_USE_COEFF }, -}; - SkXfermode* SkXfermode::Create(Mode mode) { SkASSERT(SK_ARRAY_COUNT(gProcCoeffs) == kModeCount); SkASSERT((unsigned)mode < kModeCount); diff --git a/src/effects/SkBitmapCache.cpp b/src/effects/SkBitmapCache.cpp new file mode 100644 index 0000000..2a3f87a --- /dev/null +++ b/src/effects/SkBitmapCache.cpp @@ -0,0 +1,158 @@ +/* + 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 "SkBitmapCache.h" + +struct SkBitmapCache::Entry { + Entry* fPrev; + Entry* fNext; + + void* fBuffer; + size_t fSize; + SkBitmap fBitmap; + + Entry(const void* buffer, size_t size, const SkBitmap& bm) : fBitmap(bm) { + fBuffer = sk_malloc_throw(size); + fSize = size; + memcpy(fBuffer, buffer, size); + } + + ~Entry() { sk_free(fBuffer); } + + bool equals(const void* buffer, size_t size) const { + return (fSize == size) && !memcmp(fBuffer, buffer, size); + } +}; + +SkBitmapCache::SkBitmapCache(int max) : fMaxEntries(max) { + fEntryCount = 0; + fHead = fTail = NULL; + + this->validate(); +} + +SkBitmapCache::~SkBitmapCache() { + this->validate(); + + Entry* entry = fHead; + while (entry) { + Entry* next = entry->fNext; + delete entry; + entry = next; + } +} + +SkBitmapCache::Entry* SkBitmapCache::detach(Entry* entry) const { + if (entry->fPrev) { + SkASSERT(fHead != entry); + entry->fPrev->fNext = entry->fNext; + } else { + SkASSERT(fHead == entry); + fHead = entry->fNext; + } + if (entry->fNext) { + SkASSERT(fTail != entry); + entry->fNext->fPrev = entry->fPrev; + } else { + SkASSERT(fTail == entry); + fTail = entry->fPrev; + } + return entry; +} + +void SkBitmapCache::attachToHead(Entry* entry) const { + entry->fPrev = NULL; + entry->fNext = fHead; + if (fHead) { + fHead->fPrev = entry; + } else { + fTail = entry; + } + fHead = entry; +} + +bool SkBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const { + AutoValidate av(this); + + Entry* entry = fHead; + while (entry) { + if (entry->equals(buffer, size)) { + if (bm) { + *bm = entry->fBitmap; + } + // move to the head of our list, so we purge it last + this->detach(entry); + this->attachToHead(entry); + return true; + } + entry = entry->fNext; + } + return false; +} + +void SkBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) { + AutoValidate av(this); + + if (fEntryCount == fMaxEntries) { + SkASSERT(fTail); + delete this->detach(fTail); + fEntryCount -= 1; + } + + Entry* entry = new Entry(buffer, len, bm); + this->attachToHead(entry); + fEntryCount += 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkBitmapCache::validate() const { + SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries); + + if (fEntryCount > 0) { + SkASSERT(NULL == fHead->fPrev); + SkASSERT(NULL == fTail->fNext); + + if (fEntryCount == 1) { + SkASSERT(fHead == fTail); + } else { + SkASSERT(fHead != fTail); + } + + Entry* entry = fHead; + int count = 0; + while (entry) { + count += 1; + entry = entry->fNext; + } + SkASSERT(count == fEntryCount); + + entry = fTail; + while (entry) { + count -= 1; + entry = entry->fPrev; + } + SkASSERT(0 == count); + } else { + SkASSERT(NULL == fHead); + SkASSERT(NULL == fTail); + } +} + +#endif + diff --git a/src/effects/SkBitmapCache.h b/src/effects/SkBitmapCache.h new file mode 100644 index 0000000..2c8b0c2 --- /dev/null +++ b/src/effects/SkBitmapCache.h @@ -0,0 +1,57 @@ +/* + 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 SkBitmapCache_DEFINED +#define SkBitmapCache_DEFINED + +#include "SkBitmap.h" + +class SkBitmapCache : SkNoncopyable { +public: + SkBitmapCache(int maxEntries); + ~SkBitmapCache(); + + bool find(const void* buffer, size_t len, SkBitmap*) const; + void add(const void* buffer, size_t len, const SkBitmap&); + +private: + int fEntryCount; + const int fMaxEntries; + + struct Entry; + mutable Entry* fHead; + mutable Entry* fTail; + + inline Entry* detach(Entry*) const; + inline void attachToHead(Entry*) const; + +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif + + class AutoValidate : SkNoncopyable { + public: + AutoValidate(const SkBitmapCache* bc) : fBC(bc) { bc->validate(); } + ~AutoValidate() { fBC->validate(); } + private: + const SkBitmapCache* fBC; + }; +}; + +#endif + diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp index 6ad4451..a657780 100644 --- a/src/effects/SkBlurDrawLooper.cpp +++ b/src/effects/SkBlurDrawLooper.cpp @@ -5,12 +5,20 @@ #include "SkMaskFilter.h" SkBlurDrawLooper::SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, - SkColor color) - : fDx(dx), fDy(dy), fBlurColor(color) + SkColor color, uint32_t flags) + : fDx(dx), fDy(dy), fBlurColor(color), fBlurFlags(flags) { + SkASSERT(flags <= kAll_BlurFlag); if (radius > 0) + { + uint32_t blurFlags = flags & kIgnoreTransform_BlurFlag ? + SkBlurMaskFilter::kIgnoreTransform_BlurFlag : + SkBlurMaskFilter::kNone_BlurFlag; + fBlur = SkBlurMaskFilter::Create(radius, - SkBlurMaskFilter::kNormal_BlurStyle); + SkBlurMaskFilter::kNormal_BlurStyle, + blurFlags); + } else fBlur = NULL; } @@ -21,11 +29,12 @@ SkBlurDrawLooper::SkBlurDrawLooper(SkFlattenableReadBuffer& buffer) fDy = buffer.readScalar(); fBlurColor = buffer.readU32(); fBlur = static_cast<SkMaskFilter*>(buffer.readFlattenable()); + fBlurFlags = buffer.readU32() & kAll_BlurFlag; } SkBlurDrawLooper::~SkBlurDrawLooper() { - fBlur->safeUnref(); + SkSafeUnref(fBlur); } void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) @@ -34,6 +43,7 @@ void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) buffer.writeScalar(fDy); buffer.write32(fBlurColor); buffer.writeFlattenable(fBlur); + buffer.write32(fBlurFlags); } void SkBlurDrawLooper::init(SkCanvas* canvas, SkPaint* paint) @@ -65,7 +75,16 @@ bool SkBlurDrawLooper::next() fPaint->setColor(blurColor); fPaint->setMaskFilter(fBlur); fCanvas->save(SkCanvas::kMatrix_SaveFlag); - fCanvas->translate(fDx, fDy); + if (fBlurFlags & kIgnoreTransform_BlurFlag) + { + SkMatrix transform(fCanvas->getTotalMatrix()); + transform.postTranslate(fDx, fDy); + fCanvas->setMatrix(transform); + } + else + { + fCanvas->translate(fDx, fDy); + } fState = kAfterEdge; return true; case kAfterEdge: diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp index b74cd99..8941cd1 100644 --- a/src/effects/SkBlurMaskFilter.cpp +++ b/src/effects/SkBlurMaskFilter.cpp @@ -22,7 +22,7 @@ class SkBlurMaskFilterImpl : public SkMaskFilter { public: - SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle style); + SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle style, uint32_t flags); // overrides from SkMaskFilter virtual SkMask::Format getFormat(); @@ -39,24 +39,28 @@ public: private: SkScalar fRadius; SkBlurMaskFilter::BlurStyle fBlurStyle; + uint32_t fBlurFlags; SkBlurMaskFilterImpl(SkFlattenableReadBuffer&); typedef SkMaskFilter INHERITED; }; -SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius, SkBlurMaskFilter::BlurStyle style) +SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius, SkBlurMaskFilter::BlurStyle style, + uint32_t flags) { - if (radius <= 0 || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount) + if (radius <= 0 || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount + || flags > SkBlurMaskFilter::kAll_BlurFlag) return NULL; - return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style)); + return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style, flags)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////// -SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle style) - : fRadius(radius), fBlurStyle(style) +SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle style, + uint32_t flags) + : fRadius(radius), fBlurStyle(style), fBlurFlags(flags) { #if 0 fGamma = NULL; @@ -71,6 +75,7 @@ SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::Bl #endif SkASSERT(radius >= 0); SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount); + SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag); } SkMask::Format SkBlurMaskFilterImpl::getFormat() @@ -80,7 +85,12 @@ SkMask::Format SkBlurMaskFilterImpl::getFormat() bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin) { - SkScalar radius = matrix.mapRadius(fRadius); + SkScalar radius; + if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) + radius = fRadius; + else + radius = matrix.mapRadius(fRadius); + // To avoid unseemly allocation requests (esp. for finite platforms like // handset) we limit the radius so something manageable. (as opposed to // a request like 10,000) @@ -113,6 +123,7 @@ SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer) : Sk { fRadius = buffer.readScalar(); fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readS32(); + fBlurFlags = buffer.readU32() & SkBlurMaskFilter::kAll_BlurFlag; SkASSERT(fRadius >= 0); SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount); } @@ -122,6 +133,7 @@ void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) this->INHERITED::flatten(buffer); buffer.writeScalar(fRadius); buffer.write32(fBlurStyle); + buffer.write32(fBlurFlags); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp index 733e1ae..a396d35 100644 --- a/src/effects/SkColorFilters.cpp +++ b/src/effects/SkColorFilters.cpp @@ -75,7 +75,8 @@ private: class SkSrcOver_XfermodeColorFilter : public Sk_XfermodeColorFilter { public: - SkSrcOver_XfermodeColorFilter(SkColor color) : INHERITED(color) {} + SkSrcOver_XfermodeColorFilter(SkColor color) + : INHERITED(color), fColor32Proc(SkBlitRow::ColorProcFactory()) {} virtual uint32_t getFlags() { if (SkGetPackedA32(fPMColor) == 0xFF) { @@ -87,7 +88,7 @@ public: virtual void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) { - SkBlitRow::Color32(result, shader, count, fPMColor); + fColor32Proc(result, shader, count, fPMColor); } virtual void filterSpan16(const uint16_t shader[], int count, @@ -100,7 +101,7 @@ protected: virtual Factory getFactory() { return CreateProc; } SkSrcOver_XfermodeColorFilter(SkFlattenableReadBuffer& buffer) - : INHERITED(buffer) {} + : INHERITED(buffer), fColor32Proc(SkBlitRow::ColorProcFactory()) {} private: static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { @@ -108,6 +109,7 @@ private: } typedef Sk_XfermodeColorFilter INHERITED; + SkBlitRow::ColorProc fColor32Proc; }; ////////////////////////////////////////////////////////////////////////////// diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp index 5a3a80a..64a78c7 100644 --- a/src/effects/SkGradientShader.cpp +++ b/src/effects/SkGradientShader.cpp @@ -2,23 +2,30 @@ ** ** 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 +** 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 +** 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 +** 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 "SkGradientShader.h" #include "SkColorPriv.h" +#include "SkMallocPixelRef.h" #include "SkUnitMapper.h" #include "SkUtils.h" +#include "SkTemplates.h" +#include "SkBitmapCache.h" + +#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT + #define USE_DITHER_32BIT_GRADIENT +#endif /////////////////////////////////////////////////////////////////////////// @@ -119,8 +126,9 @@ protected: const uint16_t* getCache16(); const SkPMColor* getCache32(); - // called when we kill our cached colors (to be rebuilt later on demand) - virtual void onCacheReset() = 0; + SkMallocPixelRef* fCache32PixelRef; + + void commonAsABitmap(SkBitmap*); private: enum { @@ -135,10 +143,11 @@ private: SkPMColor* fCache32; // working ptr. If this is NULL, we need to recompute the cache values uint16_t* fCache16Storage; // storage for fCache16, allocated on demand - SkPMColor* fCache32Storage; // storage for fCache32, allocated on demand unsigned fCacheAlpha; // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count); + static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count, + U8CPU alpha); typedef SkShader INHERITED; }; @@ -160,15 +169,16 @@ Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[], fCacheAlpha = 256; // init to a value that paint.getAlpha() can't return fMapper = mapper; - mapper->safeRef(); + SkSafeRef(mapper); SkASSERT((unsigned)mode < SkShader::kTileModeCount); SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs)); fTileMode = mode; fTileProc = gTileProcs[mode]; - + fCache16 = fCache16Storage = NULL; - fCache32 = fCache32Storage = NULL; + fCache32 = NULL; + fCache32PixelRef = NULL; /* Note: we let the caller skip the first and/or last position. i.e. pos[0] = 0.3, pos[1] = 0.7 @@ -276,7 +286,8 @@ Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) : fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable()); fCache16 = fCache16Storage = NULL; - fCache32 = fCache32Storage = NULL; + fCache32 = NULL; + fCache32PixelRef = NULL; int colorCount = fColorCount = buffer.readU32(); if (colorCount > kColorStorageCount) { @@ -306,13 +317,11 @@ Gradient_Shader::~Gradient_Shader() { if (fCache16Storage) { sk_free(fCache16Storage); } - if (fCache32Storage) { - sk_free(fCache32Storage); - } + SkSafeUnref(fCache32PixelRef); if (fOrigColors != fStorage) { sk_free(fOrigColors); } - fMapper->safeUnref(); + SkSafeUnref(fMapper); } void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) { @@ -377,7 +386,9 @@ bool Gradient_Shader::setContext(const SkBitmap& device, fCache32 = NULL; // inval the cache fCacheAlpha = paintAlpha; // record the new alpha // inform our subclasses - this->onCacheReset(); + if (fCache32PixelRef) { + fCache32PixelRef->notifyPixelsChanged(); + } } return true; } @@ -451,8 +462,26 @@ void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1, } while (--count != 0); } -static void build_32bit_cache(SkPMColor cache[], SkColor c0, SkColor c1, - int count, U8CPU paintAlpha) { +/* + * 2x2 dither a fixed-point color component (8.16) down to 8, matching the + * semantics of how we 2x2 dither 32->16 + */ +static inline U8CPU dither_fixed_to_8(SkFixed n) { + n >>= 8; + return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8; +} + +/* + * For dithering with premultiply, we want to ceiling the alpha component, + * to ensure that it is always >= any color component. + */ +static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) { + n >>= 8; + return ((n << 1) - (n | (n >> 8))) >> 8; +} + +void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1, + int count, U8CPU paintAlpha) { SkASSERT(count > 1); // need to apply paintAlpha to our two endpoints @@ -476,7 +505,12 @@ static void build_32bit_cache(SkPMColor cache[], SkColor c0, SkColor c1, b = SkIntToFixed(b) + 0x8000; do { - *cache++ = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16); + cache[0] = SkPreMultiplyARGB(a >> 16, r >> 16, g >> 16, b >> 16); + cache[kCache32Count] = SkPreMultiplyARGB(dither_ceil_fixed_to_8(a), + dither_fixed_to_8(r), + dither_fixed_to_8(g), + dither_fixed_to_8(b)); + cache += 1; a += da; r += dr; g += dg; @@ -503,8 +537,12 @@ static inline U16CPU bitsTo16(unsigned x, const unsigned bits) { const uint16_t* Gradient_Shader::getCache16() { if (fCache16 == NULL) { + // double the count for dither entries + const int entryCount = kCache16Count * 2; + const size_t allocSize = sizeof(uint16_t) * entryCount; + if (fCache16Storage == NULL) { // set the storage and our working ptr - fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2); + fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize); } fCache16 = fCache16Storage; if (fColorCount == 2) { @@ -524,7 +562,7 @@ const uint16_t* Gradient_Shader::getCache16() { } if (fMapper) { - fCache16Storage = (uint16_t*)sk_malloc_throw(sizeof(uint16_t) * kCache16Count * 2); + fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize); uint16_t* linear = fCache16; // just computed linear data uint16_t* mapped = fCache16Storage; // storage for mapped data SkUnitMapper* map = fMapper; @@ -542,13 +580,18 @@ const uint16_t* Gradient_Shader::getCache16() { const SkPMColor* Gradient_Shader::getCache32() { if (fCache32 == NULL) { - if (fCache32Storage == NULL) // set the storage and our working ptr - fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count); + // double the count for dither entries + const int entryCount = kCache32Count * 2; + const size_t allocSize = sizeof(SkPMColor) * entryCount; - fCache32 = fCache32Storage; + if (NULL == fCache32PixelRef) { + fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef, + (NULL, allocSize, NULL)); + } + fCache32 = (SkPMColor*)fCache32PixelRef->getAddr(); if (fColorCount == 2) { - build_32bit_cache(fCache32, fOrigColors[0], fOrigColors[1], - kCache32Count, fCacheAlpha); + Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1], + kCache32Count, fCacheAlpha); } else { Rec* rec = fRecs; int prevIndex = 0; @@ -557,29 +600,93 @@ const SkPMColor* Gradient_Shader::getCache32() { SkASSERT(nextIndex < kCache32Count); if (nextIndex > prevIndex) - build_32bit_cache(fCache32 + prevIndex, fOrigColors[i-1], - fOrigColors[i], - nextIndex - prevIndex + 1, fCacheAlpha); + Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1], + fOrigColors[i], + nextIndex - prevIndex + 1, fCacheAlpha); prevIndex = nextIndex; } SkASSERT(prevIndex == kCache32Count - 1); } if (fMapper) { - fCache32Storage = (SkPMColor*)sk_malloc_throw(sizeof(SkPMColor) * kCache32Count); + SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef, + (NULL, allocSize, NULL)); SkPMColor* linear = fCache32; // just computed linear data - SkPMColor* mapped = fCache32Storage; // storage for mapped data + SkPMColor* mapped = (SkPMColor*)newPR->getAddr(); // storage for mapped data SkUnitMapper* map = fMapper; - for (int i = 0; i < 256; i++) { - mapped[i] = linear[map->mapUnit16((i << 8) | i) >> 8]; + for (int i = 0; i < kCache32Count; i++) { + int index = map->mapUnit16((i << 8) | i) >> 8; + mapped[i] = linear[index]; + mapped[i + kCache32Count] = linear[index + kCache32Count]; } - sk_free(fCache32); - fCache32 = fCache32Storage; + fCache32PixelRef->unref(); + fCache32PixelRef = newPR; + fCache32 = (SkPMColor*)newPR->getAddr(); } } return fCache32; } +/* + * Because our caller might rebuild the same (logically the same) gradient + * over and over, we'd like to return exactly the same "bitmap" if possible, + * allowing the client to utilize a cache of our bitmap (e.g. with a GPU). + * To do that, we maintain a private cache of built-bitmaps, based on our + * colors and positions. Note: we don't try to flatten the fMapper, so if one + * is present, we skip the cache for now. + */ +void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) { + // don't have a way to put the mapper into our cache-key yet + if (fMapper) { + // force our cahce32pixelref to be built + (void)this->getCache32(); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1); + bitmap->setPixelRef(fCache32PixelRef); + return; + } + + // build our key: [numColors + colors[] + {positions[]} ] + int count = 1 + fColorCount; + if (fColorCount > 2) { + count += fColorCount - 1; // fRecs[].fPos + } + + SkAutoSTMalloc<16, int32_t> storage(count); + int32_t* buffer = storage.get(); + + *buffer++ = fColorCount; + memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor)); + buffer += fColorCount; + if (fColorCount > 2) { + for (int i = 1; i < fColorCount; i++) { + *buffer++ = fRecs[i].fPos; + } + } + SkASSERT(buffer - storage.get() == count); + + /////////////////////////////////// + + static SkMutex gMutex; + static SkBitmapCache* gCache; + // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp + static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32; + SkAutoMutexAcquire ama(gMutex); + + if (NULL == gCache) { + gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS); + } + size_t size = count * sizeof(int32_t); + + if (!gCache->find(storage.get(), size, bitmap)) { + // force our cahce32pixelref to be built + (void)this->getCache32(); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1); + bitmap->setPixelRef(fCache32PixelRef); + + gCache->add(storage.get(), size, *bitmap); + } +} + /////////////////////////////////////////////////////////////////////////// static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) { @@ -602,39 +709,24 @@ public: SkShader::TileMode mode, SkUnitMapper* mapper) : Gradient_Shader(colors, pos, colorCount, mode, mapper) { - fCachedBitmap = NULL; pts_to_unit_matrix(pts, &fPtsToUnit); } - virtual ~Linear_Gradient() { - if (fCachedBitmap) { - SkDELETE(fCachedBitmap); - } - } virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&); virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count); - virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*); - virtual void onCacheReset() { - if (fCachedBitmap) { - SkDELETE(fCachedBitmap); - fCachedBitmap = NULL; - } - } + virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, + TileMode*, SkScalar* twoPointRadialParams); - static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return SkNEW_ARGS(Linear_Gradient, (buffer)); } protected: - Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) { - fCachedBitmap = NULL; - } + Linear_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {} virtual Factory getFactory() { return CreateProc; } private: - SkBitmap* fCachedBitmap; // allocated on demand - typedef Gradient_Shader INHERITED; }; @@ -672,12 +764,18 @@ void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) SkMatrix::MapXYProc dstProc = fDstToIndexProc; TileProc proc = fTileProc; const SkPMColor* cache = this->getCache32(); +#ifdef USE_DITHER_32BIT_GRADIENT + int toggle = ((x ^ y) & 1) << kCache32Bits; + const int TOGGLE_MASK = (1 << kCache32Bits); +#else + int toggle = 0; + const int TOGGLE_MASK = 0; +#endif if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkFixed dx, fx = SkScalarToFixed(srcPt.fX); - // preround fx by half the amount we throw away - fx += 1 << 7; if (fDstToIndexClass == kFixedStepInX_MatrixClass) { SkFixed dxStorage[1]; @@ -692,43 +790,23 @@ void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) // we're a vertical gradient, so no change in a span unsigned fi = proc(fx); SkASSERT(fi <= 0xFFFF); + // TODO: dither version sk_memset32(dstC, cache[fi >> (16 - kCache32Bits)], count); } else if (proc == clamp_tileproc) { -#if 0 - if (no_need_for_clamp(fx, dx, count)) - { - unsigned fi; - while ((count -= 4) >= 0) - { - fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi]; - fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi]; - fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi]; - fi = fx >> 8; SkASSERT(fi <= 0xFF); fx += dx; *dstC++ = cache[fi]; - } - SkASSERT(count <= -1 && count >= -4); - count += 4; - while (--count >= 0) - { - fi = fx >> 8; - SkASSERT(fi <= 0xFF); - fx += dx; - *dstC++ = cache[fi]; - } - } - else -#endif - do { - unsigned fi = SkClampMax(fx >> 8, 0xFF); - SkASSERT(fi <= 0xFF); - fx += dx; - *dstC++ = cache[fi]; - } while (--count != 0); + do { + unsigned fi = SkClampMax(fx >> 8, 0xFF); + SkASSERT(fi <= 0xFF); + fx += dx; + *dstC++ = cache[toggle + fi]; + toggle ^= TOGGLE_MASK; + } while (--count != 0); } else if (proc == mirror_tileproc) { do { unsigned fi = mirror_8bits(fx >> 8); SkASSERT(fi <= 0xFF); fx += dx; - *dstC++ = cache[fi]; + *dstC++ = cache[toggle + fi]; + toggle ^= TOGGLE_MASK; } while (--count != 0); } else { SkASSERT(proc == repeat_tileproc); @@ -736,7 +814,8 @@ void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) unsigned fi = repeat_8bits(fx >> 8); SkASSERT(fi <= 0xFF); fx += dx; - *dstC++ = cache[fi]; + *dstC++ = cache[toggle + fi]; + toggle ^= TOGGLE_MASK; } while (--count != 0); } } else { @@ -746,24 +825,19 @@ void Linear_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) dstProc(fDstToIndex, dstX, dstY, &srcPt); unsigned fi = proc(SkScalarToFixed(srcPt.fX)); SkASSERT(fi <= 0xFFFF); - *dstC++ = cache[fi >> (16 - kCache32Bits)]; + *dstC++ = cache[toggle + (fi >> (16 - kCache32Bits))]; + toggle ^= TOGGLE_MASK; dstX += SK_Scalar1; } while (--count != 0); } } -bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix, - TileMode xy[]) { - // we cache our "bitmap", so it's generationID will be const on subsequent - // calls to asABitmap - if (NULL == fCachedBitmap) { - fCachedBitmap = SkNEW(SkBitmap); - fCachedBitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1); - fCachedBitmap->setPixels((void*)this->getCache32(), NULL); - } - +SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap, + SkMatrix* matrix, + TileMode xy[], + SkScalar* twoPointRadialParams) { if (bitmap) { - *bitmap = *fCachedBitmap; + this->commonAsABitmap(bitmap); } if (matrix) { matrix->setScale(SkIntToScalar(kCache32Count), SK_Scalar1); @@ -773,7 +847,7 @@ bool Linear_Gradient::asABitmap(SkBitmap* bitmap, SkMatrix* matrix, xy[0] = fTileMode; xy[1] = kClamp_TileMode; } - return true; + return kDefault_BitmapType; } static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, @@ -785,7 +859,7 @@ static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, } sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1); - + if (count & 1) { dst[count - 1] = value; } @@ -802,10 +876,9 @@ void Linear_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) int toggle = ((x ^ y) & 1) << kCache16Bits; if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkFixed dx, fx = SkScalarToFixed(srcPt.fX); - // preround fx by half the amount we throw away - fx += 1 << 7; if (fDstToIndexClass == kFixedStepInX_MatrixClass) { SkFixed dxStorage[1]; @@ -934,7 +1007,8 @@ public: if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkFixed dx, fx = SkScalarToFixed(srcPt.fX); SkFixed dy, fy = SkScalarToFixed(srcPt.fY); @@ -1017,7 +1091,8 @@ public: int toggle = ((x ^ y) & 1) << kCache16Bits; if (fDstToIndexClass != kPerspective_MatrixClass) { - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkFixed dx, fx = SkScalarToFixed(srcPt.fX); SkFixed dy, fy = SkScalarToFixed(srcPt.fY); @@ -1107,14 +1182,31 @@ public: } } - static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + virtual BitmapType asABitmap(SkBitmap* bitmap, + SkMatrix* matrix, + TileMode* xy, + SkScalar* twoPointRadialParams) { + if (bitmap) { + this->commonAsABitmap(bitmap); + } + if (matrix) { + matrix->setScale(SkIntToScalar(kCache32Count), SkIntToScalar(kCache32Count)); + matrix->preConcat(fPtsToUnit); + } + if (xy) { + xy[0] = fTileMode; + xy[1] = kClamp_TileMode; + } + return kRadial_BitmapType; + } + + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return SkNEW_ARGS(Radial_Gradient, (buffer)); } protected: Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {}; virtual Factory getFactory() { return CreateProc; } - virtual void onCacheReset() {} private: typedef Gradient_Shader INHERITED; @@ -1130,41 +1222,41 @@ private: of the desired circle (Cx, Cy) falls at some distance t along the line segment between the start point (Sx, Sy) and end point (Ex, Ey): - + Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1) Cy = (1 - t) * Sy + t * Ey - + The radius of the desired circle (r) is also a linear interpolation t between the start and end radii (Sr and Er): - + r = (1 - t) * Sr + t * Er - + But - + (x - Cx)^2 + (y - Cy)^2 = r^2 - + so - + (x - ((1 - t) * Sx + t * Ex))^2 + (y - ((1 - t) * Sy + t * Ey))^2 = ((1 - t) * Sr + t * Er)^2 - + Solving for t yields - + [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0 - + To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy [Dx^2 + Dy^2 - Dr^2)] * t^2 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t + [dx^2 + dy^2 - Sr^2] = 0 - - A quadratic in t. The two roots of the quadratic reflect the two + + A quadratic in t. The two roots of the quadratic reflect the two possible circles on which the point may fall. Solving for t yields the gradient value to use. - + If a<0, the start circle is entirely contained in the end circle, and one of the roots will be <0 or >1 (off the line segment). If a>0, the start circle falls at least partially @@ -1172,22 +1264,22 @@ private: defines a "tube" where a point may be on one circle (on the inside of the tube) or the other (outside of the tube). We choose one arbitrarily. - + In order to keep the math to within the limits of fixed point, we divide the entire quadratic by Dr^2, and replace (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving - + [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t + [x'^2 + y'^2 - Sr^2/Dr^2] = 0 - + (x' and y' are computed by appending the subtract and scale to the fDstToIndex matrix in the constructor). Since the 'A' component of the quadratic is independent of x' and y', it is precomputed in the constructor. Since the 'B' component is linear in x' and y', if x and y are linear in the span, 'B' can be computed - incrementally with a simple delta (db below). If it is not (e.g., + incrementally with a simple delta (db below). If it is not (e.g., a perspective projection), it must be computed in the loop. */ @@ -1228,6 +1320,37 @@ public: fPtsToUnit.setTranslate(-start.fX, -start.fY); fPtsToUnit.postScale(inv, inv); } + + virtual BitmapType asABitmap(SkBitmap* bitmap, + SkMatrix* matrix, + TileMode* xy, + SkScalar* twoPointRadialParams) { + if (bitmap) { + this->commonAsABitmap(bitmap); + } + SkScalar diffL = 0; // just to avoid gcc warning + if (matrix || twoPointRadialParams) { + diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) + + SkScalarSquare(fDiff.fY)); + } + if (matrix) { + SkScalar invDiffL = SkScalarInvert(diffL); + matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY), + SkScalarMul(invDiffL, fDiff.fX)); + matrix->preConcat(fPtsToUnit); + } + if (xy) { + xy[0] = fTileMode; + xy[1] = kClamp_TileMode; + } + if (NULL != twoPointRadialParams) { + twoPointRadialParams[0] = diffL; + twoPointRadialParams[1] = fStartRadius; + twoPointRadialParams[2] = fDiffRadius; + } + return kTwoPointRadial_BitmapType; + } + virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) { SkASSERT(count > 0); @@ -1250,7 +1373,8 @@ public: if (fDstToIndexClass != kPerspective_MatrixClass) { SkPoint srcPt; - dstProc(fDstToIndex, SkIntToScalar(x), SkIntToScalar(y), &srcPt); + dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkFixed dx, fx = SkScalarToFixed(srcPt.fX); SkFixed dy, fy = SkScalarToFixed(srcPt.fY); @@ -1338,7 +1462,7 @@ public: return true; } - static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer)); } @@ -1352,7 +1476,7 @@ public: buffer.writeScalar(fA); buffer.writeScalar(fOneOverTwoA); } - + protected: Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) { @@ -1365,7 +1489,6 @@ protected: fOneOverTwoA = buffer.readScalar(); }; virtual Factory getFactory() { return CreateProc; } - virtual void onCacheReset() {} private: typedef Gradient_Shader INHERITED; @@ -1385,7 +1508,24 @@ public: } virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count); virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count); - + + virtual BitmapType asABitmap(SkBitmap* bitmap, + SkMatrix* matrix, + TileMode* xy, + SkScalar* twoPointRadialParams) { + if (bitmap) { + this->commonAsABitmap(bitmap); + } + if (matrix) { + *matrix = fPtsToUnit; + } + if (xy) { + xy[0] = fTileMode; + xy[1] = kClamp_TileMode; + } + return kSweep_BitmapType; + } + static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return SkNEW_ARGS(Sweep_Gradient, (buffer)); } @@ -1393,7 +1533,6 @@ public: protected: Sweep_Gradient(SkFlattenableReadBuffer& buffer) : Gradient_Shader(buffer) {} virtual Factory getFactory() { return CreateProc; } - virtual void onCacheReset() {} private: typedef Gradient_Shader INHERITED; @@ -1413,7 +1552,7 @@ static const uint8_t* build_sweep_table() { const int N = 65; const double DENOM = N - 1; - + for (int i = 0; i < N; i++) { double arg = i / DENOM; @@ -1448,12 +1587,12 @@ static unsigned div_64(int numer, int denom) SkASSERT(numer <= denom); SkASSERT(numer > 0); SkASSERT(denom > 0); - + int nbits = SkCLZ(numer); int dbits = SkCLZ(denom); int bits = 6 - nbits + dbits; SkASSERT(bits <= 6); - + if (bits < 0) // detect underflow return 0; @@ -1467,7 +1606,7 @@ static unsigned div_64(int numer, int denom) result = 1; else numer += denom; - + // Now fall into our switch statement if there are more bits to compute if (bits > 0) { @@ -1545,7 +1684,7 @@ static unsigned atan_0_90(SkFixed y, SkFixed x) } result = div_64(y, x); - + #ifdef SK_DEBUG { unsigned result2 = SkDivBits(y, x, 6); @@ -1580,7 +1719,7 @@ static unsigned SkATan2_255(SkFixed y, SkFixed x) } if (y == 0) return x < 0 ? 128 : 0; - + /* Find the right quadrant for x,y Since atan_0_90 only handles the first quadrant, we rotate x,y appropriately before calling it, and then add the right amount @@ -1589,7 +1728,7 @@ static unsigned SkATan2_255(SkFixed y, SkFixed x) quadrant 1 : add 64 (90 degrees) | x < 0 && y > 0 quadrant 2 : add 128 (180 degrees) | x < 0 && y < 0 quadrant 3 : add 192 (270 degrees) | x > 0 && y < 0 - + map x<0 to (1 << 6) map y<0 to (3 << 6) add = map_x ^ map_y @@ -1610,7 +1749,7 @@ static unsigned SkATan2_255(SkFixed y, SkFixed x) else SkASSERT(!"bad value for add"); #endif - + /* This ^ trick makes x, y positive, and the swap<> handles quadrants where we need to rotate x,y by 90 or -90 */ @@ -1630,14 +1769,14 @@ void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) const SkMatrix& matrix = fDstToIndex; const SkPMColor* cache = this->getCache32(); SkPoint srcPt; - + if (fDstToIndexClass != kPerspective_MatrixClass) { proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkFixed dx, fx = SkScalarToFixed(srcPt.fX); SkFixed dy, fy = SkScalarToFixed(srcPt.fY); - + if (fDstToIndexClass == kFixedStepInX_MatrixClass) { SkFixed storage[2]; @@ -1652,7 +1791,7 @@ void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) dx = SkScalarToFixed(matrix.getScaleX()); dy = SkScalarToFixed(matrix.getSkewY()); } - + for (; count > 0; --count) { *dstC++ = cache[SkATan2_255(fy, fx)]; @@ -1666,7 +1805,7 @@ void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor dstC[], int count) { proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - + int index = SkATan2_255(SkScalarToFixed(srcPt.fY), SkScalarToFixed(srcPt.fX)); *dstC++ = cache[index]; @@ -1688,7 +1827,7 @@ void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkFixed dx, fx = SkScalarToFixed(srcPt.fX); SkFixed dy, fy = SkScalarToFixed(srcPt.fY); - + if (fDstToIndexClass == kFixedStepInX_MatrixClass) { SkFixed storage[2]; @@ -1703,7 +1842,7 @@ void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) dx = SkScalarToFixed(matrix.getScaleX()); dy = SkScalarToFixed(matrix.getSkewY()); } - + for (; count > 0; --count) { int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits); @@ -1719,7 +1858,7 @@ void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t dstC[], int count) { proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf, &srcPt); - + int index = SkATan2_255(SkScalarToFixed(srcPt.fY), SkScalarToFixed(srcPt.fX)); index >>= (8 - kCache16Bits); diff --git a/src/effects/SkGroupShape.cpp b/src/effects/SkGroupShape.cpp new file mode 100644 index 0000000..b8edc82 --- /dev/null +++ b/src/effects/SkGroupShape.cpp @@ -0,0 +1,128 @@ +#include "SkGroupShape.h" + +SkGroupShape::SkGroupShape() {} + +SkGroupShape::~SkGroupShape() { + this->removeAllShapes(); +} + +int SkGroupShape::countShapes() const { + return fList.count(); +} + +SkShape* SkGroupShape::getShape(int index, SkMatrixRef** mr) const { + if ((unsigned)index < (unsigned)fList.count()) { + const Rec& rec = fList[index]; + if (mr) { + *mr = rec.fMatrixRef; + } + return rec.fShape; + } + return NULL; +} + +void SkGroupShape::addShape(int index, SkShape* shape, SkMatrixRef* mr) { + int count = fList.count(); + if (NULL == shape || index < 0 || index > count) { + return; + } + + shape->ref(); + SkMatrixRef::SafeRef(mr); + + Rec* rec; + if (index == count) { + rec = fList.append(); + } else { + rec = fList.insert(index); + } + rec->fShape = shape; + rec->fMatrixRef = mr; +} + +void SkGroupShape::removeShape(int index) { + if ((unsigned)index < (unsigned)fList.count()) { + Rec& rec = fList[index]; + rec.fShape->unref(); + SkMatrixRef::SafeUnref(rec.fMatrixRef); + fList.remove(index); + } +} + +void SkGroupShape::removeAllShapes() { + Rec* rec = fList.begin(); + Rec* stop = fList.end(); + while (rec < stop) { + rec->fShape->unref(); + SkMatrixRef::SafeUnref(rec->fMatrixRef); + rec++; + } + fList.reset(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkGroupShape::onDraw(SkCanvas* canvas) { + const Rec* rec = fList.begin(); + const Rec* stop = fList.end(); + while (rec < stop) { + SkShape* shape = rec->fShape; + if (rec->fMatrixRef) { + shape->drawMatrix(canvas, *rec->fMatrixRef); + } else { + shape->draw(canvas); + } + rec++; + } +} + +SkFlattenable::Factory SkGroupShape::getFactory() { + return CreateProc; +} + +void SkGroupShape::flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + + int count = fList.count(); + buffer.write32(count); + const Rec* rec = fList.begin(); + const Rec* stop = fList.end(); + while (rec < stop) { + buffer.writeFlattenable(rec->fShape); + if (rec->fMatrixRef) { + char storage[SkMatrix::kMaxFlattenSize]; + uint32_t size = rec->fMatrixRef->flatten(storage); + buffer.write32(size); + buffer.writePad(storage, size); + } else { + buffer.write32(0); + } + rec += 1; + } +} + +SkGroupShape::SkGroupShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer){ + int count = buffer.readS32(); + for (int i = 0; i < count; i++) { + SkShape* shape = reinterpret_cast<SkShape*>(buffer.readFlattenable()); + SkMatrixRef* mr = NULL; + uint32_t size = buffer.readS32(); + if (size) { + char storage[SkMatrix::kMaxFlattenSize]; + buffer.read(storage, SkAlign4(size)); + mr = SkNEW(SkMatrixRef); + mr->unflatten(storage); + } + if (shape) { + this->appendShape(shape, mr)->unref(); + } + SkSafeUnref(mr); + } +} + +SkFlattenable* SkGroupShape::CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkGroupShape, (buffer)); +} + +static SkFlattenable::Registrar gReg("SkGroupShape", SkGroupShape::CreateProc); + diff --git a/src/effects/SkLayerRasterizer.cpp b/src/effects/SkLayerRasterizer.cpp index 5aa883d..168fbe9 100644 --- a/src/effects/SkLayerRasterizer.cpp +++ b/src/effects/SkLayerRasterizer.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -59,7 +59,7 @@ static bool compute_bounds(const SkDeque& layers, const SkPath& path, const SkMa SkLayerRasterizer_Rec* rec; bounds->set(SK_MaxS32, SK_MaxS32, SK_MinS32, SK_MinS32); - + while ((rec = (SkLayerRasterizer_Rec*)iter.next()) != NULL) { const SkPaint& paint = rec->fPaint; @@ -117,13 +117,13 @@ bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix, } if (SkMask::kJustComputeBounds_CreateMode != mode) - { + { SkBitmap device; SkDraw draw; SkMatrix translatedMatrix; // this translates us to our local pixels SkMatrix drawMatrix; // this translates the path by each layer's offset SkRegion rectClip; - + rectClip.setRect(0, 0, mask->fBounds.width(), mask->fBounds.height()); translatedMatrix = matrix; @@ -138,7 +138,7 @@ bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix, draw.fClip = &rectClip; // we set the matrixproc in the loop, as the matrix changes each time (potentially) draw.fBounder = NULL; - + SkDeque::Iter iter(fLayers); SkLayerRasterizer_Rec* rec; @@ -158,7 +158,7 @@ static void paint_read(SkPaint* paint, SkFlattenableReadBuffer& buffer) paint->setAntiAlias(buffer.readBool()); paint->setStyle((SkPaint::Style)buffer.readU8()); paint->setAlpha(buffer.readU8()); - + if (paint->getStyle() != SkPaint::kFill_Style) { paint->setStrokeWidth(buffer.readScalar()); @@ -167,10 +167,10 @@ static void paint_read(SkPaint* paint, SkFlattenableReadBuffer& buffer) paint->setStrokeJoin((SkPaint::Join)buffer.readU8()); } - paint->setMaskFilter((SkMaskFilter*)buffer.readFlattenable())->safeUnref(); - paint->setPathEffect((SkPathEffect*)buffer.readFlattenable())->safeUnref(); - paint->setRasterizer((SkRasterizer*)buffer.readFlattenable())->safeUnref(); - paint->setXfermode((SkXfermode*)buffer.readFlattenable())->safeUnref(); + SkSafeUnref(paint->setMaskFilter((SkMaskFilter*)buffer.readFlattenable())); + SkSafeUnref(paint->setPathEffect((SkPathEffect*)buffer.readFlattenable())); + SkSafeUnref(paint->setRasterizer((SkRasterizer*)buffer.readFlattenable())); + SkSafeUnref(paint->setXfermode((SkXfermode*)buffer.readFlattenable())); } static void paint_write(const SkPaint& paint, SkFlattenableWriteBuffer& buffer) @@ -178,7 +178,7 @@ static void paint_write(const SkPaint& paint, SkFlattenableWriteBuffer& buffer) buffer.writeBool(paint.isAntiAlias()); buffer.write8(paint.getStyle()); buffer.write8(paint.getAlpha()); - + if (paint.getStyle() != SkPaint::kFill_Style) { buffer.writeScalar(paint.getStrokeWidth()); @@ -186,7 +186,7 @@ static void paint_write(const SkPaint& paint, SkFlattenableWriteBuffer& buffer) buffer.write8(paint.getStrokeCap()); buffer.write8(paint.getStrokeJoin()); } - + buffer.writeFlattenable(paint.getMaskFilter()); buffer.writeFlattenable(paint.getPathEffect()); buffer.writeFlattenable(paint.getRasterizer()); @@ -197,11 +197,11 @@ SkLayerRasterizer::SkLayerRasterizer(SkFlattenableReadBuffer& buffer) : SkRasterizer(buffer), fLayers(sizeof(SkLayerRasterizer_Rec)) { int count = buffer.readS32(); - + for (int i = 0; i < count; i++) { SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back(); - + #if 0 new (&rec->fPaint) SkPaint(buffer); #else diff --git a/src/effects/SkRectShape.cpp b/src/effects/SkRectShape.cpp new file mode 100644 index 0000000..8a38a1e --- /dev/null +++ b/src/effects/SkRectShape.cpp @@ -0,0 +1,87 @@ +#include "SkRectShape.h" +#include "SkCanvas.h" + +SkPaintShape::SkPaintShape() { + fPaint.setAntiAlias(true); +} + +SkRectShape::SkRectShape() { + fBounds.setEmpty(); + fRadii.set(0, 0); +} + +void SkRectShape::setRect(const SkRect& bounds) { + fBounds = bounds; + fRadii.set(0, 0); +} + +void SkRectShape::setOval(const SkRect& bounds) { + fBounds = bounds; + fRadii.set(-SK_Scalar1, -SK_Scalar1); +} + +void SkRectShape::setCircle(SkScalar cx, SkScalar cy, SkScalar radius) { + fBounds.set(cx - radius, cy - radius, cx + radius, cy + radius); + fRadii.set(-SK_Scalar1, -SK_Scalar1); +} + +void SkRectShape::setRRect(const SkRect& bounds, SkScalar rx, SkScalar ry) { + if (rx < 0) { + rx = 0; + } + if (ry < 0) { + ry = 0; + } + + fBounds = bounds; + fRadii.set(rx, ry); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkRectShape::onDraw(SkCanvas* canvas) { + const SkPaint& paint = this->paint(); + + if (fRadii.fWidth < 0) { + canvas->drawOval(fBounds, paint); + } else if (fRadii.isZero()) { + canvas->drawRect(fBounds, paint); + } else { + canvas->drawRoundRect(fBounds, fRadii.fWidth, fRadii.fHeight, paint); + } +} + +SkFlattenable::Factory SkRectShape::getFactory() { + return CreateProc; +} + +void SkRectShape::flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + + buffer.writeRect(fBounds); + *(SkSize*)buffer.reserve(sizeof(SkSize)) = fRadii; +} + +SkRectShape::SkRectShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { + buffer.read(&fBounds, sizeof(fBounds)); + buffer.read(&fRadii, sizeof(fRadii)); +} + +SkFlattenable* SkRectShape::CreateProc(SkFlattenableReadBuffer& buffer) { + return SkNEW_ARGS(SkRectShape, (buffer)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkPaintShape::flatten(SkFlattenableWriteBuffer& buffer) { + this->INHERITED::flatten(buffer); + + fPaint.flatten(buffer); +} + +SkPaintShape::SkPaintShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { + fPaint.unflatten(buffer); +} + +static SkFlattenable::Registrar gReg("SkRectShape", SkRectShape::CreateProc); + diff --git a/src/gl/SkGL.cpp b/src/gl/SkGL.cpp deleted file mode 100644 index 1fce98f..0000000 --- a/src/gl/SkGL.cpp +++ /dev/null @@ -1,526 +0,0 @@ -#include "SkGL.h" -#include "SkColorPriv.h" -#include "SkGeometry.h" -#include "SkPaint.h" -#include "SkPath.h" -#include "SkTemplates.h" -#include "SkXfermode.h" - -//#define TRACE_TEXTURE_CREATION - -/////////////////////////////////////////////////////////////////////////////// - -#ifdef SK_GL_HAS_COLOR4UB -static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) { - glColor4ub(r, g, b, a); -} - -void SkGL::SetAlpha(U8CPU alpha) { - glColor4ub(alpha, alpha, alpha, alpha); -} -#else -static inline SkFixed byte2fixed(U8CPU value) { - return (value + (value >> 7)) << 8; -} - -static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) { - glColor4x(byte2fixed(r), byte2fixed(g), byte2fixed(b), byte2fixed(a)); -} - -void SkGL::SetAlpha(U8CPU alpha) { - SkFixed fa = byte2fixed(alpha); - glColor4x(fa, fa, fa, fa); -} -#endif - -void SkGL::SetColor(SkColor c) { - SkPMColor pm = SkPreMultiplyColor(c); - gl_pmcolor(SkGetPackedR32(pm), - SkGetPackedG32(pm), - SkGetPackedB32(pm), - SkGetPackedA32(pm)); -} - -static const GLenum gXfermodeCoeff2Blend[] = { - GL_ZERO, - GL_ONE, - GL_SRC_COLOR, - GL_ONE_MINUS_SRC_COLOR, - GL_DST_COLOR, - GL_ONE_MINUS_DST_COLOR, - GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA, -}; - -void SkGL::SetPaint(const SkPaint& paint, bool isPremul, bool justAlpha) { - if (justAlpha) { - SkGL::SetAlpha(paint.getAlpha()); - } else { - SkGL::SetColor(paint.getColor()); - } - - GLenum sm = GL_ONE; - GLenum dm = GL_ONE_MINUS_SRC_ALPHA; - - SkXfermode* mode = paint.getXfermode(); - SkXfermode::Coeff sc, dc; - if (mode && mode->asCoeff(&sc, &dc)) { - sm = gXfermodeCoeff2Blend[sc]; - dm = gXfermodeCoeff2Blend[dc]; - } - - // hack for text, which is not-premul (afaik) - if (!isPremul) { - if (GL_ONE == sm) { - sm = GL_SRC_ALPHA; - } - } - - glEnable(GL_BLEND); - glBlendFunc(sm, dm); - - if (paint.isDither()) { - glEnable(GL_DITHER); - } else { - glDisable(GL_DITHER); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGL::DumpError(const char caller[]) { - GLenum err = glGetError(); - if (err) { - SkDebugf("---- glGetError(%s) %d\n", caller, err); - } -} - -void SkGL::SetRGBA(uint8_t rgba[], const SkColor src[], int count) { - for (int i = 0; i < count; i++) { - SkPMColor c = SkPreMultiplyColor(*src++); - *rgba++ = SkGetPackedR32(c); - *rgba++ = SkGetPackedG32(c); - *rgba++ = SkGetPackedB32(c); - *rgba++ = SkGetPackedA32(c); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGL::Scissor(const SkIRect& r, int viewportHeight) { - glScissor(r.fLeft, viewportHeight - r.fBottom, r.width(), r.height()); -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGL::Ortho(float left, float right, float bottom, float top, - float near, float far) { - - float mat[16]; - - sk_bzero(mat, sizeof(mat)); - - mat[0] = 2 / (right - left); - mat[5] = 2 / (top - bottom); - mat[10] = 2 / (near - far); - mat[15] = 1; - - mat[12] = (right + left) / (left - right); - mat[13] = (top + bottom) / (bottom - top); - mat[14] = (far + near) / (near - far); - - glMultMatrixf(mat); -} - -/////////////////////////////////////////////////////////////////////////////// - -static bool canBeTexture(const SkBitmap& bm, GLenum* format, GLenum* type) { - switch (bm.config()) { - case SkBitmap::kARGB_8888_Config: - *format = GL_RGBA; - *type = GL_UNSIGNED_BYTE; - break; - case SkBitmap::kRGB_565_Config: - *format = GL_RGB; - *type = GL_UNSIGNED_SHORT_5_6_5; - break; - case SkBitmap::kARGB_4444_Config: - *format = GL_RGBA; - *type = GL_UNSIGNED_SHORT_4_4_4_4; - break; - case SkBitmap::kIndex8_Config: -#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D - *format = GL_PALETTE8_RGBA8_OES; - *type = GL_UNSIGNED_BYTE; // unused I think -#else - // we promote index to argb32 - *format = GL_RGBA; - *type = GL_UNSIGNED_BYTE; -#endif - break; - case SkBitmap::kA8_Config: - *format = GL_ALPHA; - *type = GL_UNSIGNED_BYTE; - break; - default: - return false; - } - return true; -} - -#define SK_GL_SIZE_OF_PALETTE (256 * sizeof(SkPMColor)) - -size_t SkGL::ComputeTextureMemorySize(const SkBitmap& bitmap) { - int shift = 0; - size_t adder = 0; - switch (bitmap.config()) { - case SkBitmap::kARGB_8888_Config: - case SkBitmap::kRGB_565_Config: - case SkBitmap::kARGB_4444_Config: - case SkBitmap::kA8_Config: - // we're good as is - break; - case SkBitmap::kIndex8_Config: -#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D - // account for the colortable - adder = SK_GL_SIZE_OF_PALETTE; -#else - // we promote index to argb32 - shift = 2; -#endif - break; - default: - return 0; - } - return (bitmap.getSize() << shift) + adder; -} - -#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D -/* Fill out buffer with the compressed format GL expects from a colortable - based bitmap. [palette (colortable) + indices]. - - At the moment I always take the 8bit version, since that's what my data - is. I could detect that the colortable.count is <= 16, and then repack the - indices as nibbles to save RAM, but it would take more time (i.e. a lot - slower than memcpy), so I'm skipping that for now. - - GL wants a full 256 palette entry, even though my ctable is only as big - as the colortable.count says it is. I presume it is OK to leave any - trailing entries uninitialized, since none of my indices should exceed - ctable->count(). -*/ -static void build_compressed_data(void* buffer, const SkBitmap& bitmap) { - SkASSERT(SkBitmap::kIndex8_Config == bitmap.config()); - - SkColorTable* ctable = bitmap.getColorTable(); - uint8_t* dst = (uint8_t*)buffer; - - memcpy(dst, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); - ctable->unlockColors(false); - - // always skip a full 256 number of entries, even if we memcpy'd fewer - dst += SK_GL_SIZE_OF_PALETTE; - memcpy(dst, bitmap.getPixels(), bitmap.getSize()); -} -#endif - -/* Return true if the bitmap cannot be supported in its current config as a - texture, and it needs to be promoted to ARGB32. - */ -static bool needToPromoteTo32bit(const SkBitmap& bitmap) { - if (bitmap.config() == SkBitmap::kIndex8_Config) { -#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D - const int w = bitmap.width(); - const int h = bitmap.height(); - if (SkNextPow2(w) == w && SkNextPow2(h) == h) { - // we can handle Indx8 if we're a POW2 - return false; - } -#endif - return true; // must promote to ARGB32 - } - return false; -} - -GLuint SkGL::BindNewTexture(const SkBitmap& origBitmap, SkPoint* max) { - SkBitmap tmpBitmap; - const SkBitmap* bitmap = &origBitmap; - - if (needToPromoteTo32bit(origBitmap)) { - origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config); - // now bitmap points to our temp, which has been promoted to 32bits - bitmap = &tmpBitmap; - } - - GLenum format, type; - if (!canBeTexture(*bitmap, &format, &type)) { - return 0; - } - - SkAutoLockPixels alp(*bitmap); - if (!bitmap->readyToDraw()) { - return 0; - } - - GLuint textureName; - glGenTextures(1, &textureName); - - glBindTexture(GL_TEXTURE_2D, textureName); - - // express rowbytes as a number of pixels for ow - int ow = bitmap->rowBytesAsPixels(); - int oh = bitmap->height(); - int nw = SkNextPow2(ow); - int nh = SkNextPow2(oh); - - glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); - - // check if we need to scale to create power-of-2 dimensions -#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D - if (SkBitmap::kIndex8_Config == bitmap->config()) { - size_t imagesize = bitmap->getSize() + SK_GL_SIZE_OF_PALETTE; - SkAutoMalloc storage(imagesize); - - build_compressed_data(storage.get(), *bitmap); - // we only support POW2 here (GLES 1.0 restriction) - SkASSERT(ow == nw); - SkASSERT(oh == nh); - glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0, - imagesize, storage.get()); - } else // fall through to non-compressed logic -#endif - { - if (ow != nw || oh != nh) { - glTexImage2D(GL_TEXTURE_2D, 0, format, nw, nh, 0, - format, type, NULL); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ow, oh, - format, type, bitmap->getPixels()); - } else { - // easy case, the bitmap is already pow2 - glTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0, - format, type, bitmap->getPixels()); - } - } - -#ifdef TRACE_TEXTURE_CREATION - SkDebugf("--- new texture [%d] size=(%d %d) bpp=%d\n", textureName, ow, oh, - bitmap->bytesPerPixel()); -#endif - - if (max) { - max->fX = SkFixedToScalar(bitmap->width() << (16 - SkNextLog2(nw))); - max->fY = SkFixedToScalar(oh << (16 - SkNextLog2(nh))); - } - return textureName; -} - -static const GLenum gTileMode2GLWrap[] = { - GL_CLAMP_TO_EDGE, - GL_REPEAT, -#if GL_VERSION_ES_CM_1_0 - GL_REPEAT // GLES doesn't support MIRROR -#else - GL_MIRRORED_REPEAT -#endif -}; - -void SkGL::SetTexParams(bool doFilter, - SkShader::TileMode tx, SkShader::TileMode ty) { - SkASSERT((unsigned)tx < SK_ARRAY_COUNT(gTileMode2GLWrap)); - SkASSERT((unsigned)ty < SK_ARRAY_COUNT(gTileMode2GLWrap)); - - GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST; - - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileMode2GLWrap[tx]); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileMode2GLWrap[ty]); -} - -void SkGL::SetTexParamsClamp(bool doFilter) { - GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST; - - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkGL::DrawVertices(int count, GLenum mode, - const SkGLVertex* SK_RESTRICT vertex, - const SkGLVertex* SK_RESTRICT texCoords, - const uint8_t* SK_RESTRICT colorArray, - const uint16_t* SK_RESTRICT indexArray, - SkGLClipIter* iter) { - SkASSERT(NULL != vertex); - - if (NULL != texCoords) { - glEnable(GL_TEXTURE_2D); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, SK_GLType, 0, texCoords); - } else { - glDisable(GL_TEXTURE_2D); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } - - if (NULL != colorArray) { - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, colorArray); - glShadeModel(GL_SMOOTH); - } else { - glDisableClientState(GL_COLOR_ARRAY); - glShadeModel(GL_FLAT); - } - - glVertexPointer(2, SK_GLType, 0, vertex); - - if (NULL != indexArray) { - if (iter) { - while (!iter->done()) { - iter->scissor(); - glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray); - iter->next(); - } - } else { - glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray); - } - } else { - if (iter) { - while (!iter->done()) { - iter->scissor(); - glDrawArrays(mode, 0, count); - iter->next(); - } - } else { - glDrawArrays(mode, 0, count); - } - } -} - -void SkGL::PrepareForFillPath(SkPaint* paint) { - if (paint->getStrokeWidth() <= 0) { - paint->setStrokeWidth(SK_Scalar1); - } -} - -void SkGL::FillPath(const SkPath& path, const SkPaint& paint, bool useTex, - SkGLClipIter* iter) { - SkPaint p(paint); - SkPath fillPath; - - SkGL::PrepareForFillPath(&p); - p.getFillPath(path, &fillPath); - SkGL::DrawPath(fillPath, useTex, iter); -} - -// should return max of all contours, rather than the sum (to save temp RAM) -static int worst_case_edge_count(const SkPath& path) { - int edgeCount = 0; - - SkPath::Iter iter(path, true); - SkPath::Verb verb; - - while ((verb = iter.next(NULL)) != SkPath::kDone_Verb) { - switch (verb) { - case SkPath::kLine_Verb: - edgeCount += 1; - break; - case SkPath::kQuad_Verb: - edgeCount += 8; - break; - case SkPath::kCubic_Verb: - edgeCount += 16; - break; - default: - break; - } - } - return edgeCount; -} - -void SkGL::DrawPath(const SkPath& path, bool useTex, SkGLClipIter* clipIter) { - const SkRect& bounds = path.getBounds(); - if (bounds.isEmpty()) { - return; - } - - int maxPts = worst_case_edge_count(path); - // add 1 for center of fan, and 1 for closing edge - SkAutoSTMalloc<32, SkGLVertex> storage(maxPts + 2); - SkGLVertex* base = storage.get(); - SkGLVertex* vert = base; - SkGLVertex* texs = useTex ? base : NULL; - - SkPath::Iter pathIter(path, true); - SkPoint pts[4]; - - bool needEnd = false; - - for (;;) { - switch (pathIter.next(pts)) { - case SkPath::kMove_Verb: - if (needEnd) { - SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN, - base, texs, NULL, NULL, clipIter); - clipIter->safeRewind(); - vert = base; - } - needEnd = true; - // center of the FAN - vert->setScalars(bounds.centerX(), bounds.centerY()); - vert++; - // add first edge point - vert->setPoint(pts[0]); - vert++; - break; - case SkPath::kLine_Verb: - vert->setPoint(pts[1]); - vert++; - break; - case SkPath::kQuad_Verb: { - const int n = 8; - const SkScalar dt = SK_Scalar1 / n; - SkScalar t = dt; - for (int i = 1; i < n; i++) { - SkPoint loc; - SkEvalQuadAt(pts, t, &loc, NULL); - t += dt; - vert->setPoint(loc); - vert++; - } - vert->setPoint(pts[2]); - vert++; - break; - } - case SkPath::kCubic_Verb: { - const int n = 16; - const SkScalar dt = SK_Scalar1 / n; - SkScalar t = dt; - for (int i = 1; i < n; i++) { - SkPoint loc; - SkEvalCubicAt(pts, t, &loc, NULL, NULL); - t += dt; - vert->setPoint(loc); - vert++; - } - vert->setPoint(pts[3]); - vert++; - break; - } - case SkPath::kClose_Verb: - break; - case SkPath::kDone_Verb: - goto FINISHED; - } - } -FINISHED: - if (needEnd) { - SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN, base, texs, - NULL, NULL, clipIter); - } -} - diff --git a/src/gl/SkGL.h b/src/gl/SkGL.h deleted file mode 100644 index 9f79ad1..0000000 --- a/src/gl/SkGL.h +++ /dev/null @@ -1,307 +0,0 @@ -#ifndef SkGL_DEFINED -#define SkGL_DEFINED - -#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_SDL) - #include <OpenGL/gl.h> - #include <OpenGL/glext.h> - #include <AGL/agl.h> - // use FBOs for devices - #define SK_GL_DEVICE_FBO -#elif defined(ANDROID) - #include <GLES/gl.h> - #include <EGL/egl.h> - #include <GLES/glext.h> -#endif - -#include "SkColor.h" -#include "SkMatrix.h" -#include "SkShader.h" - -class SkPaint; -class SkPath; - -class SkGLClipIter; - -//#define TRACE_TEXTURE_CREATE - -static void SkGL_unimpl(const char str[]) { - SkDebugf("SkGL unimplemented: %s\n", str); -} -/////////////////////////////////////////////////////////////////////////////// - -#if GL_OES_compressed_paletted_texture - #define SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D -#endif - -#if GL_OES_fixed_point && defined(SK_SCALAR_IS_FIXED) - #define SK_GLType GL_FIXED -#else - #define SK_GLType GL_FLOAT -#endif - -#if SK_GLType == GL_FIXED - typedef SkFixed SkGLScalar; - - #define SkIntToGL(n) SkIntToFixed(n) - #define SkScalarToGL(x) SkScalarToFixed(x) - #define SK_GLScalar1 SK_Fixed1 - #define SkGLScalarMul(a, b) SkFixedMul(a, b) - #define MAKE_GL(name) name ## x - - #ifdef SK_SCALAR_IS_FIXED - #define GLSCALAR_IS_SCALAR 1 - #define SkPerspToGL(x) SkFractToFixed(x) - #else - #define GLSCALAR_IS_SCALAR 0 - #define SkPerspToGL(x) SkFractToFloat(x) - #endif -#else - typedef float SkGLScalar; - - #define SkIntToGL(n) (n) - #define SkScalarToGL(x) SkScalarToFloat(x) - #define SK_GLScalar1 (1.f) - #define SkGLScalarMul(a, b) ((a) * (b)) - #define MAKE_GL(name) name ## f - - #ifdef SK_SCALAR_IS_FLOAT - #define GLSCALAR_IS_SCALAR 1 - #define SkPerspToGL(x) (x) - #else - #define GLSCALAR_IS_SCALAR 0 - #define SkPerspToGL(x) SkFractToFloat(x) - #endif -#endif - -#if GL_OES_fixed_point - typedef SkFixed SkGLTextScalar; - #define SK_TextGLType GL_FIXED - - #define SkIntToTextGL(n) SkIntToFixed(n) - #define SkFixedToTextGL(x) (x) - - #define SK_glTexParameteri(target, pname, param) \ - glTexParameterx(target, pname, param) -#else - typedef float SkGLTextScalar; - #define SK_TextGLType SK_GLType - #define SK_GL_HAS_COLOR4UB - - #define SkIntToTextGL(n) SkIntToGL(n) - #define SkFixedToTextGL(x) SkFixedToFloat(x) - - - #define SK_glTexParameteri(target, pname, param) \ - glTexParameteri(target, pname, param) -#endif - -/////////////////////////////////////////////////////////////////////////////// - -// text has its own vertex class, since it may want to be in fixed point (given) -// that it starts with all integers) even when the default vertices are floats -struct SkGLTextVertex { - SkGLTextScalar fX; - SkGLTextScalar fY; - - void setI(int x, int y) { - fX = SkIntToTextGL(x); - fY = SkIntToTextGL(y); - } - - void setX(SkFixed x, SkFixed y) { - fX = SkFixedToTextGL(x); - fY = SkFixedToTextGL(y); - } - - // counter-clockwise fan - void setIRectFan(int l, int t, int r, int b) { - SkGLTextVertex* SK_RESTRICT v = this; - v[0].setI(l, t); - v[1].setI(l, b); - v[2].setI(r, b); - v[3].setI(r, t); - } - - // counter-clockwise fan - void setXRectFan(SkFixed l, SkFixed t, SkFixed r, SkFixed b) { - SkGLTextVertex* SK_RESTRICT v = this; - v[0].setX(l, t); - v[1].setX(l, b); - v[2].setX(r, b); - v[3].setX(r, t); - } -}; - -struct SkGLVertex { - SkGLScalar fX; - SkGLScalar fY; - - void setGL(SkGLScalar x, SkGLScalar y) { - fX = x; - fY = y; - } - - void setScalars(SkScalar x, SkScalar y) { - fX = SkScalarToGL(x); - fY = SkScalarToGL(y); - } - - void setPoint(const SkPoint& pt) { - fX = SkScalarToGL(pt.fX); - fY = SkScalarToGL(pt.fY); - } - - void setPoints(const SkPoint* SK_RESTRICT pts, int count) { - const SkScalar* SK_RESTRICT src = (const SkScalar*)pts; - SkGLScalar* SK_RESTRICT dst = (SkGLScalar*)this; - for (int i = 0; i < count; i++) { - *dst++ = SkScalarToGL(*src++); - *dst++ = SkScalarToGL(*src++); - } - } - - // counter-clockwise fan - void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b) { - SkGLVertex* v = this; - v[0].setScalars(l, t); - v[1].setScalars(l, b); - v[2].setScalars(r, b); - v[3].setScalars(r, t); - } - - // counter-clockwise fan - void setIRectFan(int l, int t, int r, int b) { - SkGLVertex* v = this; - v[0].setGL(SkIntToGL(l), SkIntToGL(t)); - v[1].setGL(SkIntToGL(l), SkIntToGL(b)); - v[2].setGL(SkIntToGL(r), SkIntToGL(b)); - v[3].setGL(SkIntToGL(r), SkIntToGL(t)); - } - - // counter-clockwise fan - void setRectFan(const SkRect& r) { - this->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom); - } - - // counter-clockwise fan - void setIRectFan(const SkIRect& r) { - this->setIRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom); - } -}; - -struct SkGLMatrix { - SkGLScalar fMat[16]; - - void reset() { - sk_bzero(fMat, sizeof(fMat)); - fMat[0] = fMat[5] = fMat[10] = fMat[15] = SK_GLScalar1; - } - - void set(const SkMatrix& m) { - sk_bzero(fMat, sizeof(fMat)); - fMat[0] = SkScalarToGL(m[SkMatrix::kMScaleX]); - fMat[4] = SkScalarToGL(m[SkMatrix::kMSkewX]); - fMat[12] = SkScalarToGL(m[SkMatrix::kMTransX]); - - fMat[1] = SkScalarToGL(m[SkMatrix::kMSkewY]); - fMat[5] = SkScalarToGL(m[SkMatrix::kMScaleY]); - fMat[13] = SkScalarToGL(m[SkMatrix::kMTransY]); - - fMat[3] = SkPerspToGL(m[SkMatrix::kMPersp0]); - fMat[7] = SkPerspToGL(m[SkMatrix::kMPersp1]); - fMat[15] = SkPerspToGL(m[SkMatrix::kMPersp2]); - - fMat[10] = SK_GLScalar1; // z-scale - } -}; - -class SkGL { -public: - static void SetColor(SkColor c); - static void SetAlpha(U8CPU alpha); - static void SetPaint(const SkPaint&, bool isPremul = true, - bool justAlpha = false); - static void SetPaintAlpha(const SkPaint& paint, bool isPremul = true) { - SetPaint(paint, isPremul, true); - } - - static void SetRGBA(uint8_t rgba[], const SkColor src[], int count); - static void DumpError(const char caller[]); - - static void Ortho(float left, float right, float bottom, float top, - float near, float far); - - static inline void Translate(SkScalar dx, SkScalar dy) { - MAKE_GL(glTranslate)(SkScalarToGL(dx), SkScalarToGL(dy), 0); - } - - static inline void Scale(SkScalar sx, SkScalar sy) { - MAKE_GL(glScale)(SkScalarToGL(sx), SkScalarToGL(sy), SK_GLScalar1); - } - - static inline void Rotate(SkScalar angle) { - MAKE_GL(glRotate)(SkScalarToGL(angle), 0, 0, SK_GLScalar1); - } - - static inline void MultMatrix(const SkMatrix& m) { - SkGLMatrix glm; - glm.set(m); - MAKE_GL(glMultMatrix)(glm.fMat); - } - - static inline void LoadMatrix(const SkMatrix& m) { - SkGLMatrix glm; - glm.set(m); - MAKE_GL(glLoadMatrix)(glm.fMat); - } - - static void Scissor(const SkIRect&, int viewportHeight); - - // return the byte size for the associated texture memory. This doesn't - // always == bitmap.getSize(), since on a given port we may have to change - // the format when the bitmap's pixels are copied over to GL - static size_t ComputeTextureMemorySize(const SkBitmap&); - // return 0 on failure - static GLuint BindNewTexture(const SkBitmap&, SkPoint* dimension); - - static void SetTexParams(bool filter, - SkShader::TileMode tx, SkShader::TileMode ty); - static void SetTexParamsClamp(bool filter); - - static void DrawVertices(int count, GLenum mode, - const SkGLVertex* SK_RESTRICT vertex, - const SkGLVertex* SK_RESTRICT texCoords, - const uint8_t* SK_RESTRICT colorArray, - const uint16_t* SK_RESTRICT indexArray, - SkGLClipIter*); - - static void PrepareForFillPath(SkPaint* paint); - static void FillPath(const SkPath& path, const SkPaint& paint, bool useTex, - SkGLClipIter*); - static void DrawPath(const SkPath& path, bool useTex, SkGLClipIter*); -}; - -#include "SkRegion.h" - -class SkGLClipIter : public SkRegion::Iterator { -public: - SkGLClipIter(int viewportHeight) : fViewportHeight(viewportHeight) {} - - // call rewind only if this is non-null - void safeRewind() { - if (this) { - this->rewind(); - } - } - - void scissor() { - SkASSERT(!this->done()); - SkGL::Scissor(this->rect(), fViewportHeight); - } - -private: - const int fViewportHeight; -}; - -#endif - diff --git a/src/gl/SkGLCanvas.cpp b/src/gl/SkGLCanvas.cpp deleted file mode 100644 index 17e6016..0000000 --- a/src/gl/SkGLCanvas.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 "SkGLCanvas.h" -#include "SkGLDevice.h" - -SkGLCanvas::SkGLCanvas() : SkCanvas(SkNEW(SkGLDeviceFactory)) {} - -// static -size_t SkGLCanvas::GetTextureCacheMaxCount() { - return SkGLDevice::GetTextureCacheMaxCount(); -} - -// static -void SkGLCanvas::SetTextureCacheMaxCount(size_t count) { - SkGLDevice::SetTextureCacheMaxCount(count); -} - -// static -size_t SkGLCanvas::GetTextureCacheMaxSize() { - return SkGLDevice::GetTextureCacheMaxSize(); -} - -// static -void SkGLCanvas::SetTextureCacheMaxSize(size_t size) { - SkGLDevice::SetTextureCacheMaxSize(size); -} - -// static -void SkGLCanvas::DeleteAllTextures() { - SkGLDevice::DeleteAllTextures(); -} - -// static -void SkGLCanvas::AbandonAllTextures() { - SkGLDevice::AbandonAllTextures(); -} diff --git a/src/gl/SkGLDevice.cpp b/src/gl/SkGLDevice.cpp deleted file mode 100644 index ad4377c..0000000 --- a/src/gl/SkGLDevice.cpp +++ /dev/null @@ -1,953 +0,0 @@ -#include "SkGLDevice.h" -#include "SkGL.h" -#include "SkDrawProcs.h" -#include "SkRegion.h" -#include "SkThread.h" - -#ifdef SK_GL_DEVICE_FBO - #define USE_FBO_DEVICE - #include "SkGLDevice_FBO.h" -#else - #define USE_SWLAYER_DEVICE - #include "SkGLDevice_SWLayer.h" -#endif - -// maximum number of entries in our texture cache (before purging) -#define kTexCountMax_Default 256 -// maximum number of bytes used (by gl) for the texture cache (before purging) -#define kTexSizeMax_Default (4 * 1024 * 1024) - -static void TRACE_DRAW(const char func[], SkGLDevice* device, - const SkDraw& draw) { - // SkDebugf("--- <%s> %p %p\n", func, canvas, draw.fDevice); -} - -struct SkGLDrawProcs : public SkDrawProcs { -public: - void init(const SkRegion* clip, int height) { - fCurrQuad = 0; - fCurrTexture = 0; - fClip = clip; - fViewportHeight = height; - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, SK_TextGLType, 0, fTexs); - glDisableClientState(GL_COLOR_ARRAY); - glVertexPointer(2, SK_TextGLType, 0, fVerts); - } - - GLenum texture() const { return fCurrTexture; } - - void flush() { - if (fCurrQuad && fCurrTexture) { - this->drawQuads(); - } - fCurrQuad = 0; - } - - void addQuad(GLuint texture, int x, int y, const SkGlyph& glyph, - SkFixed left, SkFixed right, SkFixed bottom) { - SkASSERT((size_t)fCurrQuad <= SK_ARRAY_COUNT(fVerts)); - - if (fCurrTexture != texture || fCurrQuad == SK_ARRAY_COUNT(fVerts)) { - if (fCurrQuad && fCurrTexture) { - this->drawQuads(); - } - fCurrQuad = 0; - fCurrTexture = texture; - } - - fVerts[fCurrQuad].setIRectFan(x, y, - x + glyph.fWidth, y + glyph.fHeight); - fTexs[fCurrQuad].setXRectFan(left, 0, right, bottom); - fCurrQuad += 4; - } - - void drawQuads(); - -private: - enum { - MAX_QUADS = 32 - }; - - SkGLTextVertex fVerts[MAX_QUADS * 4]; - SkGLTextVertex fTexs[MAX_QUADS * 4]; - - // these are initialized in setupForText - GLuint fCurrTexture; - int fCurrQuad; - int fViewportHeight; - const SkRegion* fClip; -}; - -/////////////////////////////////////////////////////////////////////////////// - -SkDevice* SkGLDeviceFactory::newDevice(SkBitmap::Config config, int width, - int height, bool isOpaque, - bool isForLayer) { - SkBitmap bitmap; - - bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); - bitmap.setIsOpaque(isOpaque); - -#ifdef USE_FBO_DEVICE - return SkNEW_ARGS(SkGLDevice_FBO, (bitmap, isForLayer)); -#elif defined(USE_SWLAYER_DEVICE) - if (isForLayer) { - bitmap.allocPixels(); - if (!bitmap.isOpaque()) { - bitmap.eraseColor(0); - } - return SkNEW_ARGS(SkGLDevice_SWLayer, (bitmap)); - } else { - return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer)); - } -#else - return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer)); -#endif -} - -SkGLDevice::SkGLDevice(const SkBitmap& bitmap, bool offscreen) - : SkDevice(bitmap), fClipIter(bitmap.height()) { - glEnable(GL_TEXTURE_2D); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - fDrawProcs = NULL; -} - -SkGLDevice::~SkGLDevice() { - if (fDrawProcs) { - SkDELETE(fDrawProcs); - } -} - -void SkGLDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) { - this->INHERITED::setMatrixClip(matrix, clip); - - fGLMatrix.set(matrix); - fMatrix = matrix; - fClip = clip; - fDirty = true; -} - -SkGLDevice::TexOrientation SkGLDevice::bindDeviceAsTexture() { - return kNo_TexOrientation; -} - -void SkGLDevice::gainFocus(SkCanvas* canvas) { - this->INHERITED::gainFocus(canvas); - - const int w = this->width(); - const int h = this->height(); - glViewport(0, 0, w, h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - SkGL::Ortho(0, w, h, 0, -1, 1); - glMatrixMode(GL_MODELVIEW); - fDirty = true; -} - -SkGLClipIter* SkGLDevice::updateMatrixClip() { - bool useIter = false; - - // first handle the clip - if (fDirty || !fClip.isRect()) { - fClipIter.reset(fClip); - useIter = true; - } else if (fDirty) { - // no iter means caller is not respecting complex clips :( - SkGL::Scissor(fClip.getBounds(), this->height()); - } - // else we're just a rect, and we've already call scissor - - // now handle the matrix - if (fDirty) { - MAKE_GL(glLoadMatrix)(fGLMatrix.fMat); -#if 0 - SkDebugf("--- gldevice update matrix %p %p\n", this, fFBO); - for (int y = 0; y < 4; y++) { - SkDebugf(" [ "); - for (int x = 0; x < 4; x++) { - SkDebugf("%g ", fGLMatrix.fMat[y*4 + x]); - } - SkDebugf("]\n"); - } -#endif - fDirty = false; - } - - return useIter ? &fClipIter : NULL; -} - -/////////////////////////////////////////////////////////////////////////////// - -// must be in the same order as SkXfermode::Coeff in SkXfermode.h -SkGLDevice::AutoPaintShader::AutoPaintShader(SkGLDevice* device, - const SkPaint& paint) { - fDevice = device; - fTexCache = device->setupGLPaintShader(paint); -} - -SkGLDevice::AutoPaintShader::~AutoPaintShader() { - if (fTexCache) { - SkGLDevice::UnlockTexCache(fTexCache); - } -} - -SkGLDevice::TexCache* SkGLDevice::setupGLPaintShader(const SkPaint& paint) { - SkGL::SetPaint(paint); - - SkShader* shader = paint.getShader(); - if (NULL == shader) { - return NULL; - } - - if (!shader->setContext(this->accessBitmap(false), paint, this->matrix())) { - return NULL; - } - - SkBitmap bitmap; - SkMatrix matrix; - SkShader::TileMode tileModes[2]; - if (!shader->asABitmap(&bitmap, &matrix, tileModes)) { - SkGL_unimpl("shader->asABitmap() == false"); - return NULL; - } - - bitmap.lockPixels(); - if (!bitmap.readyToDraw()) { - return NULL; - } - - // see if we've already cached the bitmap from the shader - SkPoint max; - GLuint name; - TexCache* cache = SkGLDevice::LockTexCache(bitmap, &name, &max); - // the lock has already called glBindTexture for us - SkGL::SetTexParams(paint.isFilterBitmap(), tileModes[0], tileModes[1]); - - // 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 - SkMatrix localM; - if (shader->getLocalMatrix(&localM)) { - SkMatrix inverse; - if (localM.invert(&inverse)) { - matrix.preConcat(inverse); - } - } - - matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height()); - glMatrixMode(GL_TEXTURE); - SkGL::LoadMatrix(matrix); - glMatrixMode(GL_MODELVIEW); - - // since we're going to use a shader/texture, we don't want the color, - // just its alpha - SkGL::SetAlpha(paint.getAlpha()); - // report that we have setup the texture - return cache; -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -void SkGLDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { - TRACE_DRAW("coreDrawPaint", this, draw); - - AutoPaintShader shader(this, paint); - SkGLVertex vertex[4]; - const SkGLVertex* texs = shader.useTex() ? vertex : NULL; - - // set vert to be big enough to fill the space, but not super-huge, to we - // don't overflow fixed-point implementations - { - SkRect r; - r.set(this->clip().getBounds()); - SkMatrix inverse; - if (draw.fMatrix->invert(&inverse)) { - inverse.mapRect(&r); - } - vertex->setRectFan(r); - } - - SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL, - this->updateMatrixClip()); -} - -// must be in SkCanvas::PointMode order -static const GLenum gPointMode2GL[] = { - GL_POINTS, - GL_LINES, - GL_LINE_STRIP -}; - -void SkGLDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, - size_t count, const SkPoint pts[], const SkPaint& paint) { - TRACE_DRAW("coreDrawPoints", this, draw); - - SkScalar width = paint.getStrokeWidth(); - if (width < 0) { - return; - } - - /* We should really only use drawverts for hairlines, since gl and skia - treat the thickness differently... - */ - - AutoPaintShader shader(this, paint); - - if (width <= 0) { - width = SK_Scalar1; - } - - if (SkCanvas::kPoints_PointMode == mode) { - glPointSize(SkScalarToFloat(width)); - } else { - glLineWidth(SkScalarToFloat(width)); - } - - const SkGLVertex* verts; - -#if GLSCALAR_IS_SCALAR - verts = (const SkGLVertex*)pts; -#else - SkAutoSTMalloc<32, SkGLVertex> storage(count); - SkGLVertex* v = storage.get(); - - v->setPoints(pts, count); - verts = v; -#endif - - const SkGLVertex* texs = shader.useTex() ? verts : NULL; - - SkGL::DrawVertices(count, gPointMode2GL[mode], verts, texs, NULL, NULL, - this->updateMatrixClip()); -} - -/* create a triangle strip that strokes the specified triangle. There are 8 - unique vertices, but we repreat the last 2 to close up. Alternatively we - could use an indices array, and then only send 8 verts, but not sure that - would be faster. - */ -static void setStrokeRectStrip(SkGLVertex verts[10], const SkRect& rect, - SkScalar width) { - const SkScalar rad = SkScalarHalf(width); - - verts[0].setScalars(rect.fLeft + rad, rect.fTop + rad); - verts[1].setScalars(rect.fLeft - rad, rect.fTop - rad); - verts[2].setScalars(rect.fRight - rad, rect.fTop + rad); - verts[3].setScalars(rect.fRight + rad, rect.fTop - rad); - verts[4].setScalars(rect.fRight - rad, rect.fBottom - rad); - verts[5].setScalars(rect.fRight + rad, rect.fBottom + rad); - verts[6].setScalars(rect.fLeft + rad, rect.fBottom - rad); - verts[7].setScalars(rect.fLeft - rad, rect.fBottom + rad); - verts[8] = verts[0]; - verts[9] = verts[1]; -} - -void SkGLDevice::drawRect(const SkDraw& draw, const SkRect& rect, - const SkPaint& paint) { - TRACE_DRAW("coreDrawRect", this, draw); - - bool doStroke = paint.getStyle() == SkPaint::kStroke_Style; - - if (doStroke) { - if (paint.getStrokeJoin() != SkPaint::kMiter_Join) { - SkGL_unimpl("non-miter stroke rect"); - return; - } - } else if (paint.getStrokeJoin() != SkPaint::kMiter_Join) { - SkPath path; - path.addRect(rect); - this->drawPath(draw, path, paint); - return; - } - - AutoPaintShader shader(this, paint); - SkScalar width = paint.getStrokeWidth(); - SkGLVertex vertex[10]; // max needed for all cases - int vertCount; - GLenum vertMode; - - if (doStroke) { - if (width > 0) { - vertCount = 10; - vertMode = GL_TRIANGLE_STRIP; - setStrokeRectStrip(vertex, rect, width); - } else { // hairline - vertCount = 5; - vertMode = GL_LINE_STRIP; - vertex[0].setScalars(rect.fLeft, rect.fTop); - vertex[1].setScalars(rect.fRight, rect.fTop); - vertex[2].setScalars(rect.fRight, rect.fBottom); - vertex[3].setScalars(rect.fLeft, rect.fBottom); - vertex[4].setScalars(rect.fLeft, rect.fTop); - glLineWidth(1); - } - } else { - vertCount = 4; - vertMode = GL_TRIANGLE_FAN; - vertex->setRectFan(rect); - } - - const SkGLVertex* texs = shader.useTex() ? vertex : NULL; - SkGL::DrawVertices(vertCount, vertMode, vertex, texs, NULL, NULL, - this->updateMatrixClip()); -} - -void SkGLDevice::drawPath(const SkDraw& draw, const SkPath& path, - const SkPaint& paint) { - TRACE_DRAW("coreDrawPath", this, draw); - if (paint.getStyle() == SkPaint::kStroke_Style) { - SkGL_unimpl("stroke path"); - return; - } - - AutoPaintShader shader(this, paint); - - SkGL::FillPath(path, paint, shader.useTex(), this->updateMatrixClip()); -} - -void SkGLDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, - const SkMatrix& m, const SkPaint& paint) { - TRACE_DRAW("coreDrawBitmap", this, draw); - - SkAutoLockPixels alp(bitmap); - if (!bitmap.readyToDraw()) { - return; - } - - SkGLClipIter* iter = this->updateMatrixClip(); - - SkPoint max; - GLenum name; - SkAutoLockTexCache(bitmap, &name, &max); - // the lock has already called glBindTexture for us - SkGL::SetTexParamsClamp(paint.isFilterBitmap()); - - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - SkGL::MultMatrix(m); - - SkGLVertex pts[4], tex[4]; - - pts->setIRectFan(0, 0, bitmap.width(), bitmap.height()); - tex->setRectFan(0, 0, max.fX, max.fY); - - // now draw the mesh - SkGL::SetPaintAlpha(paint); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter); - - glPopMatrix(); -} - -// move this guy into SkGL, so we can call it from SkGLDevice -static void gl_drawSprite(int x, int y, int w, int h, const SkPoint& max, - const SkPaint& paint, SkGLClipIter* iter) { - SkGL::SetTexParamsClamp(false); - - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - SkGLVertex pts[4], tex[4]; - - // if h < 0, then the texture is bottom-to-top, but since our projection - // matrix always inverts Y, we have to re-invert our texture coord here - if (h < 0) { - h = -h; - tex->setRectFan(0, max.fY, max.fX, 0); - } else { - tex->setRectFan(0, 0, max.fX, max.fY); - } - pts->setIRectFan(x, y, x + w, y + h); - - SkGL::SetPaintAlpha(paint); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - // should look to use glDrawTexi() has we do for text... - SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter); - - glPopMatrix(); -} - -void SkGLDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, - int left, int top, const SkPaint& paint) { - TRACE_DRAW("coreDrawSprite", this, draw); - - SkAutoLockPixels alp(bitmap); - if (!bitmap.readyToDraw()) { - return; - } - - SkGLClipIter* iter = this->updateMatrixClip(); - - SkPoint max; - GLuint name; - SkAutoLockTexCache(bitmap, &name, &max); - - gl_drawSprite(left, top, bitmap.width(), bitmap.height(), max, paint, iter); -} - -void SkGLDevice::drawDevice(const SkDraw& draw, SkDevice* dev, - int x, int y, const SkPaint& paint) { - TRACE_DRAW("coreDrawDevice", this, draw); - - SkGLDevice::TexOrientation to = ((SkGLDevice*)dev)->bindDeviceAsTexture(); - if (SkGLDevice::kNo_TexOrientation != to) { - SkGLClipIter* iter = this->updateMatrixClip(); - - const SkBitmap& bm = dev->accessBitmap(false); - int w = bm.width(); - int h = bm.height(); - SkPoint max; - - max.set(SkFixedToScalar(w << (16 - SkNextLog2(bm.rowBytesAsPixels()))), - SkFixedToScalar(h << (16 - SkNextLog2(h)))); - - if (SkGLDevice::kBottomToTop_TexOrientation == to) { - h = -h; - } - gl_drawSprite(x, y, w, h, max, paint, iter); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -static const GLenum gVertexModeToGL[] = { - GL_TRIANGLES, // kTriangles_VertexMode, - GL_TRIANGLE_STRIP, // kTriangleStrip_VertexMode, - GL_TRIANGLE_FAN // kTriangleFan_VertexMode -}; - -#include "SkShader.h" - -void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, - int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], - SkXfermode* xmode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) { - - if (false) { - SkRect bounds; - SkIRect ibounds; - - bounds.set(vertices, vertexCount); - bounds.round(&ibounds); - - SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n", - vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height()); - } - - SkGLClipIter* iter = this->updateMatrixClip(); - - SkGL::SetPaint(paint); - - const SkGLVertex* glVerts; - const SkGLVertex* glTexs = NULL; - -#if GLSCALAR_IS_SCALAR - glVerts = (const SkGLVertex*)vertices; -#else - SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount); - storage.get()->setPoints(vertices, vertexCount); - glVerts = storage.get(); -#endif - - uint8_t* colorArray = NULL; - if (colors) { - colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4); - SkGL::SetRGBA(colorArray, colors, vertexCount); - } - SkAutoFree afca(colorArray); - - SkGLVertex* texArray = NULL; - TexCache* cache = NULL; - - if (texs && paint.getShader()) { - SkShader* shader = paint.getShader(); - - // if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) { - if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) { - goto DONE; - } - - SkBitmap bitmap; - SkMatrix matrix; - SkShader::TileMode tileModes[2]; - if (shader->asABitmap(&bitmap, &matrix, tileModes)) { - SkPoint max; - GLuint name; - cache = SkGLDevice::LockTexCache(bitmap, &name, &max); - if (NULL == cache) { - return; - } - - matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height()); - glMatrixMode(GL_TEXTURE); - SkGL::LoadMatrix(matrix); - glMatrixMode(GL_MODELVIEW); - -#if GLSCALAR_IS_SCALAR - glTexs = (const SkGLVertex*)texs; -#else - texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex)); - texArray->setPoints(texs, vertexCount); - glTexs = texArray; -#endif - - SkGL::SetPaintAlpha(paint); - SkGL::SetTexParams(paint.isFilterBitmap(), - tileModes[0], tileModes[1]); - } - } -DONE: - SkAutoFree aftex(texArray); - - SkGL::DrawVertices(indices ? indexCount : vertexCount, - gVertexModeToGL[vmode], - glVerts, glTexs, colorArray, indices, iter); - - if (cache) { - SkGLDevice::UnlockTexCache(cache); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -#include "SkGlyphCache.h" -#include "SkGLTextCache.h" - -void SkGLDevice::GlyphCacheAuxProc(void* data) { - SkDebugf("-------------- delete text texture cache\n"); - SkDELETE((SkGLTextCache*)data); -} - -#ifdef SK_SCALAR_IS_FIXED -#define SkDiv16ToScalar(numer, denom) (SkIntToFixed(numer) / (denom)) -#else -#define SkDiv16ToScalar(numer, denom) SkScalarDiv(numer, denom) -#endif - -// stolen from SkDraw.cpp - D1G_NoBounder_RectClip -static void SkGL_Draw1Glyph(const SkDraw1Glyph& state, const SkGlyph& glyph, - int x, int y) { - SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); - - SkGLDrawProcs* procs = (SkGLDrawProcs*)state.fDraw->fProcs; - - x += glyph.fLeft; - y += glyph.fTop; - - // check if we're clipped out (nothing to draw) - SkIRect bounds; - bounds.set(x, y, x + glyph.fWidth, y + glyph.fHeight); - if (!SkIRect::Intersects(state.fClip->getBounds(), bounds)) { - return; - } - - // now dig up our texture cache - - SkGlyphCache* gcache = state.fCache; - void* auxData; - SkGLTextCache* textCache = NULL; - - if (gcache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) { - textCache = (SkGLTextCache*)auxData; - } - if (NULL == textCache) { - // need to create one - textCache = SkNEW(SkGLTextCache); - gcache->setAuxProc(SkGLDevice::GlyphCacheAuxProc, textCache); - } - - int offset; - SkGLTextCache::Strike* strike = textCache->findGlyph(glyph, &offset); - if (NULL == strike) { - // make sure the glyph has an image - uint8_t* aa = (uint8_t*)glyph.fImage; - if (NULL == aa) { - aa = (uint8_t*)gcache->findImage(glyph); - if (NULL == aa) { - return; // can't rasterize glyph - } - } - strike = textCache->addGlyphAndBind(glyph, aa, &offset); - if (NULL == strike) { - SkGL_unimpl("addGlyphAndBind failed, too big"); - // too big to cache, need to draw as is... - return; - } - } - - const int shiftW = strike->widthShift(); - const int shiftH = strike->heightShift(); - - SkFixed left = offset << (16 - shiftW); - SkFixed right = (offset + glyph.fWidth) << (16 - shiftW); - SkFixed bottom = glyph.fHeight << (16 - shiftH); - - procs->addQuad(strike->texture(), x, y, glyph, left, right, bottom); -} - -#if 1 -// matches the orientation used in SkGL::setRectFan. Too bad we can't rely on -// QUADS in android's GL -static const uint8_t gQuadIndices[] = { - 0, 1, 2, 0, 2, 3, - 4, 5, 6, 4, 6, 7, - 8, 9, 10, 8, 10, 11, - 12, 13, 14, 12, 14, 15, - 16, 17, 18, 16, 18, 19, - 20, 21, 22, 20, 22, 23, - 24, 25, 26, 24, 26, 27, - 28, 29, 30, 28, 30, 31, - 32, 33, 34, 32, 34, 35, - 36, 37, 38, 36, 38, 39, - 40, 41, 42, 40, 42, 43, - 44, 45, 46, 44, 46, 47, - 48, 49, 50, 48, 50, 51, - 52, 53, 54, 52, 54, 55, - 56, 57, 58, 56, 58, 59, - 60, 61, 62, 60, 62, 63, - 64, 65, 66, 64, 66, 67, - 68, 69, 70, 68, 70, 71, - 72, 73, 74, 72, 74, 75, - 76, 77, 78, 76, 78, 79, - 80, 81, 82, 80, 82, 83, - 84, 85, 86, 84, 86, 87, - 88, 89, 90, 88, 90, 91, - 92, 93, 94, 92, 94, 95, - 96, 97, 98, 96, 98, 99, - 100, 101, 102, 100, 102, 103, - 104, 105, 106, 104, 106, 107, - 108, 109, 110, 108, 110, 111, - 112, 113, 114, 112, 114, 115, - 116, 117, 118, 116, 118, 119, - 120, 121, 122, 120, 122, 123, - 124, 125, 126, 124, 126, 127 -}; -#else -static void generateQuadIndices(int n) { - int index = 0; - for (int i = 0; i < n; i++) { - SkDebugf(" %3d, %3d, %3d, %3d, %3d, %3d,\n", - index, index + 1, index + 2, index, index + 2, index + 3); - index += 4; - } -} -#endif - -void SkGLDrawProcs::drawQuads() { - SkASSERT(SK_ARRAY_COUNT(gQuadIndices) == MAX_QUADS * 6); - - glBindTexture(GL_TEXTURE_2D, fCurrTexture); - -#if 0 - static bool gOnce; - if (!gOnce) { - generateQuadIndices(MAX_QUADS); - gOnce = true; - } -#endif - - // convert from quad vertex count to triangle vertex count - // 6/4 * n == n + (n >> 1) since n is always a multiple of 4 - SkASSERT((fCurrQuad & 3) == 0); - int count = fCurrQuad + (fCurrQuad >> 1); - - if (fClip->isComplex()) { - SkGLClipIter iter(fViewportHeight); - iter.reset(*fClip); - while (!iter.done()) { - iter.scissor(); - glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices); - iter.next(); - } - } else { - glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices); - } -} - -void SkGLDevice::setupForText(SkDraw* draw, const SkPaint& paint) { - // we handle complex clips in the SkDraw common code, so we don't check - // for it here - this->updateMatrixClip(); - - SkGL::SetPaint(paint, false); - - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - // deferred allocation - if (NULL == fDrawProcs) { - fDrawProcs = SkNEW(SkGLDrawProcs); - fDrawProcs->fD1GProc = SkGL_Draw1Glyph; - } - - // init our (and GL's) state - fDrawProcs->init(draw->fClip, this->height()); - // assign to the caller's SkDraw - draw->fProcs = fDrawProcs; - - glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glShadeModel(GL_FLAT); -} - -void SkGLDevice::drawText(const SkDraw& draw, const void* text, - size_t byteLength, SkScalar x, SkScalar y, - const SkPaint& paint) { - /* Currently, perspective text is draw via paths, invoked directly by - SkDraw. This can't work for us, since the bitmap that our draw points - to has no pixels, so we just abort if we're in perspective. - - Better fix would be to... - - have a callback inside draw to handle path drawing - - option to have draw call the font cache, which we could patch (?) - */ - if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) { - SkGL_unimpl("drawText in perspective"); - return; - } - - SkDraw myDraw(draw); - this->setupForText(&myDraw, paint); - this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint); - fDrawProcs->flush(); - glPopMatrix(); // GL_MODELVIEW -} - -void SkGLDevice::drawPosText(const SkDraw& draw, const void* text, - size_t byteLength, const SkScalar pos[], - SkScalar constY, int scalarsPerPos, - const SkPaint& paint) { - if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) { - SkGL_unimpl("drawPosText in perspective"); - return; - } - - SkDraw myDraw(draw); - this->setupForText(&myDraw, paint); - this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY, - scalarsPerPos, paint); - fDrawProcs->flush(); - glPopMatrix(); // GL_MODELVIEW -} - -void SkGLDevice::drawTextOnPath(const SkDraw& draw, const void* text, - size_t byteLength, const SkPath& path, - const SkMatrix* m, const SkPaint& paint) { - SkGL_unimpl("drawTextOnPath"); -} - -/////////////////////////////////////////////////////////////////////////////// - -#include "SkTextureCache.h" -#include "SkThread.h" - -static SkMutex gTextureCacheMutex; -static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default); - -SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap, - GLuint* name, SkPoint* size) { - SkAutoMutexAcquire amc(gTextureCacheMutex); - - SkTextureCache::Entry* entry = gTextureCache.lock(bitmap); - if (NULL != entry) { - if (name) { - *name = entry->name(); - } - if (size) { - *size = entry->texSize(); - } - } - return (TexCache*)entry; -} - -void SkGLDevice::UnlockTexCache(TexCache* cache) { - SkAutoMutexAcquire amc(gTextureCacheMutex); - gTextureCache.unlock((SkTextureCache::Entry*)cache); -} - -// public exposure of texture cache settings - -size_t SkGLDevice::GetTextureCacheMaxCount() { - SkAutoMutexAcquire amc(gTextureCacheMutex); - return gTextureCache.getMaxCount(); -} - -size_t SkGLDevice::GetTextureCacheMaxSize() { - SkAutoMutexAcquire amc(gTextureCacheMutex); - return gTextureCache.getMaxSize(); -} - -void SkGLDevice::SetTextureCacheMaxCount(size_t count) { - SkAutoMutexAcquire amc(gTextureCacheMutex); - gTextureCache.setMaxCount(count); -} - -void SkGLDevice::SetTextureCacheMaxSize(size_t size) { - SkAutoMutexAcquire amc(gTextureCacheMutex); - gTextureCache.setMaxSize(size); -} - -/////////////////////////////////////////////////////////////////////////////// - -#include "SkGLTextCache.h" - -static bool deleteCachesProc(SkGlyphCache* cache, void* texturesAreValid) { - void* auxData; - if (cache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) { - bool valid = texturesAreValid != NULL; - SkGLTextCache* textCache = static_cast<SkGLTextCache*>(auxData); - // call this before delete, in case valid is false - textCache->deleteAllStrikes(valid); - // now free the memory for the cache itself - SkDELETE(textCache); - // now remove the entry in the glyphcache (does not call the proc) - cache->removeAuxProc(SkGLDevice::GlyphCacheAuxProc); - } - return false; // keep going -} - -void SkGLDevice::DeleteAllTextures() { - // free the textures in our cache - - gTextureCacheMutex.acquire(); - gTextureCache.deleteAllCaches(true); - gTextureCacheMutex.release(); - - // now free the textures in the font cache - - SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(true) -); -} - -void SkGLDevice::AbandonAllTextures() { - // abandon the textures in our cache - - gTextureCacheMutex.acquire(); - gTextureCache.deleteAllCaches(false); - gTextureCacheMutex.release(); - - // abandon the textures in the font cache - - SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(false -)); -} - diff --git a/src/gl/SkGLDevice.h b/src/gl/SkGLDevice.h deleted file mode 100644 index a939b07..0000000 --- a/src/gl/SkGLDevice.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef SkGLDevice_DEFINED -#define SkGLDevice_DEFINED - -#include "SkDevice.h" -#include "SkGL.h" -#include "SkRegion.h" - -#ifdef SK_BUILD_FOR_MAC - #include <OpenGL/gl.h> -#elif defined(ANDROID) - #include <GLES/gl.h> -#endif - -class SkGLDeviceFactory : public SkDeviceFactory { -public: - virtual SkDevice* newDevice(SkBitmap::Config config, int width, int height, - bool isOpaque, bool isForLayer); -}; - -struct SkGLDrawProcs; - -class SkGLDevice : public SkDevice { -public: - SkGLDevice(const SkBitmap& bitmap, bool offscreen); - virtual ~SkGLDevice(); - - virtual SkDeviceFactory* getDeviceFactory() { - return SkNEW(SkGLDeviceFactory); - } - - virtual uint32_t getDeviceCapabilities() { return kGL_Capability; } - - // used to identify GLTextCache data in the glyphcache - static void GlyphCacheAuxProc(void* data); - - enum TexOrientation { - kNo_TexOrientation, - kTopToBottom_TexOrientation, - kBottomToTop_TexOrientation - }; - - /** Called when this device is no longer a candidate for a render target, - but will instead be used as a texture to be drawn. Be sure to call - the base impl if you override, as it will compute size and max. - */ - virtual TexOrientation bindDeviceAsTexture(); - - // returns true if complex - SkGLClipIter* updateMatrixClip(); - // call to set the clip to the specified rect - void scissor(const SkIRect&); - - // overrides from SkDevice - virtual void gainFocus(SkCanvas*); - virtual void setMatrixClip(const SkMatrix& matrix, const SkRegion& clip); - - 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& path, - const SkPaint& paint); - virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, - 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&); - - // settings for the global texture cache - - static size_t GetTextureCacheMaxCount(); - static void SetTextureCacheMaxCount(size_t count); - - static size_t GetTextureCacheMaxSize(); - static void SetTextureCacheMaxSize(size_t size); - - /** Call glDeleteTextures for all textures (including those for text) - This should be called while the gl-context is still valid. Its purpose - is to free up gl resources. Note that if a bitmap or text is drawn after - this call, new caches will be created. - */ - static void DeleteAllTextures(); - - /** Forget all textures without calling delete (including those for text). - This should be called if the gl-context has changed, and the texture - IDs that have been cached are no longer valid. - */ - static void AbandonAllTextures(); - -protected: - /** Return the current glmatrix, from a previous call to setMatrixClip */ - const SkMatrix& matrix() const { return fMatrix; } - /** Return the current clip, from a previous call to setMatrixClip */ - const SkRegion& clip() const { return fClip; } - -private: - SkGLMatrix fGLMatrix; - SkMatrix fMatrix; - SkRegion fClip; - bool fDirty; - - SkGLClipIter fClipIter; - SkGLDrawProcs* fDrawProcs; - - void setupForText(SkDraw* draw, const SkPaint& paint); - - // global texture cache methods - class TexCache; - static TexCache* LockTexCache(const SkBitmap&, GLuint* name, - SkPoint* size); - static void UnlockTexCache(TexCache*); - class SkAutoLockTexCache { - public: - SkAutoLockTexCache(const SkBitmap& bitmap, GLuint* name, - SkPoint* size) { - fTex = SkGLDevice::LockTexCache(bitmap, name, size); - } - ~SkAutoLockTexCache() { - if (fTex) { - SkGLDevice::UnlockTexCache(fTex); - } - } - TexCache* get() const { return fTex; } - private: - TexCache* fTex; - }; - friend class SkAutoTexCache; - - // returns cache if the texture is bound for the shader - TexCache* setupGLPaintShader(const SkPaint& paint); - - class AutoPaintShader { - public: - AutoPaintShader(SkGLDevice*, const SkPaint& paint); - ~AutoPaintShader(); - - bool useTex() const { return fTexCache != 0; } - private: - SkGLDevice* fDevice; - TexCache* fTexCache; - }; - friend class AutoPaintShader; - - typedef SkDevice INHERITED; -}; - -#endif - diff --git a/src/gl/SkGLDevice_FBO.cpp b/src/gl/SkGLDevice_FBO.cpp deleted file mode 100644 index 552d619..0000000 --- a/src/gl/SkGLDevice_FBO.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "SkGLDevice_FBO.h" -#include "SkRegion.h" - -SkGLDevice_FBO::SkGLDevice_FBO(const SkBitmap& bitmap, bool offscreen) - : SkGLDevice(bitmap, offscreen) { - fFBO = 0; - fTextureID = 0; - - if (offscreen) { - int nw = SkNextPow2(bitmap.rowBytesAsPixels()); - int nh = SkNextPow2(bitmap.height()); - - glGenFramebuffersEXT(1, &fFBO); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFBO); - - glGenTextures(1, &fTextureID); - glBindTexture(GL_TEXTURE_2D, fTextureID); - SkGL::SetTexParamsClamp(false); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nw, nh, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, fTextureID, 0); - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { - SkDebugf("-- glCheckFramebufferStatusEXT %x\n", status); - } - - // now reset back to "normal" drawing target - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - } -} - -SkGLDevice_FBO::~SkGLDevice_FBO() { - if (fTextureID) { - glDeleteTextures(1, &fTextureID); - } - if (fFBO) { - glDeleteFramebuffersEXT(1, &fFBO); - } -} - -SkGLDevice::TexOrientation SkGLDevice_FBO::bindDeviceAsTexture() { - if (fTextureID) { - glBindTexture(GL_TEXTURE_2D, fTextureID); - return kBottomToTop_TexOrientation; - } - return kNo_TexOrientation; -} - -void SkGLDevice_FBO::gainFocus(SkCanvas* canvas) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFBO); - - // now we're ready for the viewport and projection matrix - this->INHERITED::gainFocus(canvas); -} - diff --git a/src/gl/SkGLDevice_FBO.h b/src/gl/SkGLDevice_FBO.h deleted file mode 100644 index d695ff0..0000000 --- a/src/gl/SkGLDevice_FBO.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef SkGLDevice_FBO_DEFINED -#define SkGLDevice_FBO_DEFINED - -#include "SkGLDevice.h" - -class SkGLDevice_FBO : public SkGLDevice { -public: - SkGLDevice_FBO(const SkBitmap& bitmap, bool offscreen); - virtual ~SkGLDevice_FBO(); - - // overrides from SkGLDevice - virtual void gainFocus(SkCanvas*); - virtual TexOrientation bindDeviceAsTexture(); - -private: - GLuint fFBO; - GLuint fTextureID; - - typedef SkGLDevice INHERITED; -}; - -#endif - diff --git a/src/gl/SkGLDevice_SWLayer.cpp b/src/gl/SkGLDevice_SWLayer.cpp deleted file mode 100644 index 4b75d4c..0000000 --- a/src/gl/SkGLDevice_SWLayer.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "SkGLDevice_SWLayer.h" -#include "SkRegion.h" - -SkGLDevice_SWLayer::SkGLDevice_SWLayer(const SkBitmap& bitmap) - : SkGLDevice(bitmap, true) { - fTextureID = 0; - - SkASSERT(bitmap.getPixels()); -} - -SkGLDevice_SWLayer::~SkGLDevice_SWLayer() { - if (fTextureID) { - glDeleteTextures(1, &fTextureID); - } -} - -SkGLDevice::TexOrientation SkGLDevice_SWLayer::bindDeviceAsTexture() { - const SkBitmap& bitmap = this->accessBitmap(false); - - if (0 == fTextureID) { - fTextureID = SkGL::BindNewTexture(bitmap, NULL); - } - return kTopToBottom_TexOrientation; -} - -/////////////////////////////////////////////////////////////////////////////// - -#include "SkDraw.h" - -void SkGLDevice_SWLayer::drawPaint(const SkDraw& draw, const SkPaint& paint) { - draw.drawPaint(paint); -} - -void SkGLDevice_SWLayer::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count, - const SkPoint pts[], const SkPaint& paint) { - draw.drawPoints(mode, count, pts, paint); -} - -void SkGLDevice_SWLayer::drawRect(const SkDraw& draw, const SkRect& r, - const SkPaint& paint) { - draw.drawRect(r, paint); -} - -void SkGLDevice_SWLayer::drawPath(const SkDraw& draw, const SkPath& path, - const SkPaint& paint) { - draw.drawPath(path, paint); -} - -void SkGLDevice_SWLayer::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, - const SkMatrix& matrix, const SkPaint& paint) { - draw.drawBitmap(bitmap, matrix, paint); -} - -void SkGLDevice_SWLayer::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, - int x, int y, const SkPaint& paint) { - draw.drawSprite(bitmap, x, y, paint); -} - -void SkGLDevice_SWLayer::drawText(const SkDraw& draw, const void* text, size_t len, - SkScalar x, SkScalar y, const SkPaint& paint) { - draw.drawText((const char*)text, len, x, y, paint); -} - -void SkGLDevice_SWLayer::drawPosText(const SkDraw& draw, const void* text, size_t len, - const SkScalar xpos[], SkScalar y, - int scalarsPerPos, const SkPaint& paint) { - draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint); -} - -void SkGLDevice_SWLayer::drawTextOnPath(const SkDraw& draw, const void* text, - size_t len, const SkPath& path, - const SkMatrix* matrix, - const SkPaint& paint) { - draw.drawTextOnPath((const char*)text, len, path, matrix, paint); -} - -void SkGLDevice_SWLayer::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, - int vertexCount, - const SkPoint verts[], const SkPoint textures[], - const SkColor colors[], SkXfermode* xmode, - const uint16_t indices[], int indexCount, - const SkPaint& paint) { - draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode, - indices, indexCount, paint); -} - -void SkGLDevice_SWLayer::drawDevice(const SkDraw& draw, SkDevice* dev, - int x, int y, const SkPaint& paint) { - this->SkDevice::drawDevice(draw, dev, x, y, paint); -} - diff --git a/src/gl/SkGLDevice_SWLayer.h b/src/gl/SkGLDevice_SWLayer.h deleted file mode 100644 index 7e61370..0000000 --- a/src/gl/SkGLDevice_SWLayer.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef SkGLDevice_SWLayer_DEFINED -#define SkGLDevice_SWLayer_DEFINED - -#include "SkGLDevice.h" - -class SkGLDevice_SWLayer : public SkGLDevice { -public: - SkGLDevice_SWLayer(const SkBitmap& bitmap); - virtual ~SkGLDevice_SWLayer(); - - // overrides from SkGLDevice - virtual TexOrientation bindDeviceAsTexture(); - - // overrides from SkDevice - 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& path, - const SkPaint& paint); - virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, - 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&); - -private: - GLuint fTextureID; - - typedef SkGLDevice INHERITED; -}; - -#endif - diff --git a/src/gl/SkGLTextCache.cpp b/src/gl/SkGLTextCache.cpp deleted file mode 100644 index 2619dc8..0000000 --- a/src/gl/SkGLTextCache.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "SkGLTextCache.h" -#include "SkScalerContext.h" -#include "SkTSearch.h" - -const GLenum gTextTextureFormat = GL_ALPHA; -const GLenum gTextTextureType = GL_UNSIGNED_BYTE; - -SkGLTextCache::Strike::Strike(Strike* next, int width, int height) { - fStrikeWidth = SkNextPow2(SkMax32(kMinStrikeWidth, width)); - fStrikeHeight = SkNextPow2(height); - fGlyphCount = 0; - fNextFreeOffsetX = 0; - fNext = next; - - fStrikeWidthShift = SkNextLog2(fStrikeWidth); - fStrikeHeightShift = SkNextLog2(fStrikeHeight); - - if (next) { - SkASSERT(next->fStrikeHeight == fStrikeHeight); - } - - // create an empty texture to receive glyphs - fTexName = 0; - glGenTextures(1, &fTexName); - glBindTexture(GL_TEXTURE_2D, fTexName); - glTexImage2D(GL_TEXTURE_2D, 0, gTextTextureFormat, - fStrikeWidth, fStrikeHeight, 0, - gTextTextureFormat, gTextTextureType, NULL); - - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - -SkGLTextCache::Strike::~Strike() { - if (fTexName != 0) { - glDeleteTextures(1, &fTexName); - } -} - -SkGLTextCache::Strike* -SkGLTextCache::Strike::findGlyph(const SkGlyph& glyph, int* offset) { - Strike* strike = this; - SkDEBUGCODE(const int height = SkNextPow2(glyph.fHeight);) - - do { - SkASSERT(height == strike->fStrikeHeight); - - int index = SkTSearch(strike->fGlyphIDArray, strike->fGlyphCount, - glyph.fID, sizeof(strike->fGlyphIDArray[0])); - if (index >= 0) { - if (offset) { - *offset = strike->fGlyphOffsetX[index]; - } - return strike; - } - strike = strike->fNext; - } while (NULL != strike); - return NULL; -} - -static void make_a_whole(void* buffer, int index, int count, size_t elemSize) { - SkASSERT(index >= 0 && index <= count); - size_t offset = index * elemSize; - memmove((char*)buffer + offset + elemSize, - (const char*)buffer + offset, - (count - index) * elemSize); -} - -SkGLTextCache::Strike* -SkGLTextCache::Strike::addGlyphAndBind(const SkGlyph& glyph, - const uint8_t image[], int* offset) { -#ifdef SK_DEBUG - SkASSERT(this->findGlyph(glyph, NULL) == NULL); - const int height = SkNextPow2(glyph.fHeight); - SkASSERT(height <= fStrikeHeight && height > (fStrikeHeight >> 1)); -#endif - - int rowBytes = glyph.rowBytes(); - SkASSERT(rowBytes >= glyph.fWidth); - - Strike* strike; - if (fGlyphCount == kMaxGlyphCount || - fNextFreeOffsetX + rowBytes >= fStrikeWidth) { - // this will bind the next texture for us -// SkDebugf("--- extend strike %p\n", this); - strike = SkNEW_ARGS(Strike, (this, rowBytes, glyph.fHeight)); - } else { - glBindTexture(GL_TEXTURE_2D, fTexName); - strike = this; - } - - uint32_t* idArray = strike->fGlyphIDArray; - uint16_t* offsetArray = strike->fGlyphOffsetX; - const int glyphCount = strike->fGlyphCount; - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexSubImage2D(GL_TEXTURE_2D, 0, strike->fNextFreeOffsetX, 0, rowBytes, - glyph.fHeight, gTextTextureFormat, gTextTextureType, - image); - - // need to insert the offset - int index = SkTSearch(idArray, glyphCount, glyph.fID, sizeof(idArray[0])); - SkASSERT(index < 0); - index = ~index; // this is where we should insert it - make_a_whole(idArray, index, glyphCount, sizeof(idArray)); - make_a_whole(offsetArray, index, glyphCount, sizeof(offsetArray[0])); - idArray[index] = glyph.fID; - offsetArray[index] = strike->fNextFreeOffsetX; - if (offset) { - *offset = strike->fNextFreeOffsetX; - } - -#if 0 - SkDebugf("--- strike %p glyph %x [%d %d] offset %d count %d\n", - strike, glyph.fID, glyph.fWidth, glyph.fHeight, - strike->fNextFreeOffsetX, glyphCount + 1); -#endif - - // now update our header - strike->fGlyphCount = glyphCount + 1; - strike->fNextFreeOffsetX += glyph.fWidth; - return strike; -} - -/////////////////////////////////////////////////////////////////////////////// - -SkGLTextCache::SkGLTextCache() { - sk_bzero(fStrikeList, sizeof(fStrikeList)); -} - -SkGLTextCache::~SkGLTextCache() { - this->deleteAllStrikes(true); -} - -void SkGLTextCache::deleteAllStrikes(bool texturesAreValid) { - for (size_t i = 0; i < SK_ARRAY_COUNT(fStrikeList); i++) { - Strike* strike = fStrikeList[i]; - while (strike != NULL) { - Strike* next = strike->fNext; - if (!texturesAreValid) { - strike->abandonTexture(); - } - SkDELETE(strike); - strike = next; - } - } - sk_bzero(fStrikeList, sizeof(fStrikeList)); -} - -SkGLTextCache::Strike* SkGLTextCache::findGlyph(const SkGlyph& glyph, - int* offset) { - SkASSERT(glyph.fWidth != 0); - SkASSERT(glyph.fHeight != 0); - - size_t index = SkNextLog2(glyph.fHeight); - if (index >= SK_ARRAY_COUNT(fStrikeList)) { - // too big for us to cache; - return NULL; - } - - Strike* strike = fStrikeList[index]; - if (strike) { - strike = strike->findGlyph(glyph, offset); - } - return strike; -} - -SkGLTextCache::Strike* SkGLTextCache::addGlyphAndBind(const SkGlyph& glyph, - const uint8_t image[], int* offset) { - SkASSERT(image != NULL); - SkASSERT(glyph.fWidth != 0); - SkASSERT(glyph.fHeight != 0); - - size_t index = SkNextLog2(glyph.fHeight); - if (index >= SK_ARRAY_COUNT(fStrikeList)) { - // too big for us to cache; - return NULL; - } - - Strike* strike = fStrikeList[index]; - if (NULL == strike) { - strike = SkNEW_ARGS(Strike, (NULL, glyph.rowBytes(), glyph.fHeight)); -// SkDebugf("--- create strike [%d] %p cache %p\n", index, strike, this); - } - strike = strike->addGlyphAndBind(glyph, image, offset); - fStrikeList[index] = strike; - return strike; -} - diff --git a/src/gl/SkGLTextCache.h b/src/gl/SkGLTextCache.h deleted file mode 100644 index eb552aa..0000000 --- a/src/gl/SkGLTextCache.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef SkGLTextCache_DEFINED -#define SkGLTextCache_DEFINED - -#include "SkGL.h" - -class SkGlyph; - -class SkGLTextCache { -public: - SkGLTextCache(); - ~SkGLTextCache(); - - /** Delete all of the strikes in the cache. Pass true if the texture IDs are - still valid, in which case glDeleteTextures will be called. Pass false - if they are invalid (e.g. the gl-context has changed), in which case - they will just be abandoned. - */ - void deleteAllStrikes(bool texturesAreValid); - - class Strike { - public: - int width() const { return fStrikeWidth; } - int height() const { return fStrikeHeight; } - GLuint texture() const { return fTexName; } - int widthShift() const { return fStrikeWidthShift; } - int heightShift() const { return fStrikeHeightShift; } - - // call this to force us to ignore the texture name in our destructor - // only call it right before our destructor - void abandonTexture() { fTexName = 0; } - - private: - // if next is non-null, its height must match our height - Strike(Strike* next, int width, int height); - ~Strike(); - - Strike* findGlyph(const SkGlyph&, int* offset); - Strike* addGlyphAndBind(const SkGlyph&, const uint8_t*, int* offset); - - enum { - kMinStrikeWidth = 1024, - kMaxGlyphCount = 256 - }; - - Strike* fNext; - GLuint fTexName; - uint32_t fGlyphIDArray[kMaxGlyphCount]; // stores glyphIDs - uint16_t fGlyphOffsetX[kMaxGlyphCount]; // stores x-offsets - uint16_t fGlyphCount; - uint16_t fNextFreeOffsetX; - uint16_t fStrikeWidth; - uint16_t fStrikeHeight; - uint8_t fStrikeWidthShift; // pow2(fStrikeWidth) - uint8_t fStrikeHeightShift; // pow2(fStrikeHeight) - - friend class SkGLTextCache; - }; - - /** If found, returns the exact strike containing it (there may be more than - one with a given height), and sets offset to the offset for that glyph - (if not null). Does NOT bind the texture. - If not found, returns null and ignores offset param. - */ - Strike* findGlyph(const SkGlyph&, int* offset); - - /** Adds the specified glyph to this list of strikes, returning the new - head of the list. If offset is not null, it is set to the offset - for this glyph within the strike. The associated texture is bound - to the gl context. - */ - Strike* addGlyphAndBind(const SkGlyph&, const uint8_t image[], int* offset); - -private: - enum { - // greater than this we won't cache - kMaxGlyphHeightShift = 9, - - kMaxGlyphHeight = 1 << kMaxGlyphHeightShift, - kMaxStrikeListCount = kMaxGlyphHeightShift + 1 - }; - - // heads of the N families, one for each pow2 height - Strike* fStrikeList[kMaxStrikeListCount]; -}; - -#endif diff --git a/src/gl/SkTextureCache.cpp b/src/gl/SkTextureCache.cpp deleted file mode 100644 index 2eb5d84..0000000 --- a/src/gl/SkTextureCache.cpp +++ /dev/null @@ -1,363 +0,0 @@ -#include "SkTextureCache.h" - -//#define TRACE_HASH_HITS -//#define TRACE_TEXTURE_CACHE_PURGE - -SkTextureCache::Entry::Entry(const SkBitmap& bitmap) - : fName(0), fKey(bitmap), fPrev(NULL), fNext(NULL) { - - fMemSize = SkGL::ComputeTextureMemorySize(bitmap); - fLockCount = 0; -} - -SkTextureCache::Entry::~Entry() { - if (fName != 0) { - glDeleteTextures(1, &fName); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -SkTextureCache::SkTextureCache(size_t countMax, size_t sizeMax) - : fHead(NULL), fTail(NULL), - fTexCountMax(countMax), fTexSizeMax(sizeMax), - fTexCount(0), fTexSize(0) { - - sk_bzero(fHash, sizeof(fHash)); - this->validate(); -} - -SkTextureCache::~SkTextureCache() { -#ifdef SK_DEBUG - Entry* entry = fHead; - while (entry) { - SkASSERT(entry->lockCount() == 0); - entry = entry->fNext; - } -#endif - this->validate(); -} - -void SkTextureCache::deleteAllCaches(bool texturesAreValid) { - this->validate(); - - Entry* entry = fHead; - while (entry) { - Entry* next = entry->fNext; - if (!texturesAreValid) { - entry->abandonTexture(); - } - SkDELETE(entry); - entry = next; - } - - fSorted.reset(); - sk_bzero(fHash, sizeof(fHash)); - - fTexCount = 0; - fTexSize = 0; - - fTail = fHead = NULL; - - this->validate(); -} - -/////////////////////////////////////////////////////////////////////////////// - -int SkTextureCache::findInSorted(const Key& key) const { - int count = fSorted.count(); - if (count == 0) { - return ~0; - } - - Entry** sorted = fSorted.begin(); - int lo = 0; - int hi = count - 1; - while (lo < hi) { - int mid = (hi + lo) >> 1; - if (sorted[mid]->getKey() < key) { - lo = mid + 1; - } else { - hi = mid; - } - } - - // hi is now our best guess - const Entry* entry = sorted[hi]; - if (entry->getKey() == key) { - return hi; - } - - // return where to insert it - if (entry->getKey() < key) { - hi += 1; - } - return ~hi; // we twiddle to indicate not-found -} - -#ifdef TRACE_HASH_HITS -static int gHashHits; -static int gSortedHits; -#endif - -SkTextureCache::Entry* SkTextureCache::find(const Key& key, int* insert) const { - int count = fSorted.count(); - if (count == 0) { - *insert = 0; - return NULL; - } - - // check the hash first - int hashIndex = key.getHashIndex(); - Entry* entry = fHash[hashIndex]; - if (NULL != entry && entry->getKey() == key) { -#ifdef TRACE_HASH_HITS - gHashHits += 1; -#endif - return entry; - } - - int index = this->findInSorted(key); - if (index >= 0) { -#ifdef TRACE_HASH_HITS - gSortedHits += 1; -#endif - entry = fSorted[index]; - fHash[hashIndex] = entry; - return entry; - } - - // ~index is where to insert the entry - *insert = ~index; - return NULL; -} - -SkTextureCache::Entry* SkTextureCache::lock(const SkBitmap& bitmap) { - this->validate(); - - // call this before we call find(), so we don't reorder after find() and - // invalidate our index - this->purgeIfNecessary(SkGL::ComputeTextureMemorySize(bitmap)); - - Key key(bitmap); - int index; - Entry* entry = this->find(key, &index); - - if (NULL == entry) { - entry = SkNEW_ARGS(Entry, (bitmap)); - - entry->fName = SkGL::BindNewTexture(bitmap, &entry->fTexSize); - if (0 == entry->fName) { - SkDELETE(entry); - return NULL; - } - fHash[key.getHashIndex()] = entry; - *fSorted.insert(index) = entry; - - fTexCount += 1; - fTexSize += entry->memSize(); - } else { - // detach from our llist - Entry* prev = entry->fPrev; - Entry* next = entry->fNext; - if (prev) { - prev->fNext = next; - } else { - SkASSERT(fHead == entry); - fHead = next; - } - if (next) { - next->fPrev = prev; - } else { - SkASSERT(fTail == entry); - fTail = prev; - } - // now bind the texture - glBindTexture(GL_TEXTURE_2D, entry->fName); - } - - // add to head of llist for LRU - entry->fPrev = NULL; - entry->fNext = fHead; - if (NULL != fHead) { - SkASSERT(NULL == fHead->fPrev); - fHead->fPrev = entry; - } - fHead = entry; - if (NULL == fTail) { - fTail = entry; - } - - this->validate(); - entry->lock(); - -#ifdef TRACE_HASH_HITS - SkDebugf("---- texture cache hash=%d sorted=%d\n", gHashHits, gSortedHits); -#endif - return entry; -} - -void SkTextureCache::unlock(Entry* entry) { - this->validate(); - -#ifdef SK_DEBUG - SkASSERT(entry); - int index = this->findInSorted(entry->getKey()); - SkASSERT(fSorted[index] == entry); -#endif - - SkASSERT(entry->fLockCount > 0); - entry->unlock(); -} - -void SkTextureCache::purgeIfNecessary(size_t extraSize) { - this->validate(); - - size_t countMax = fTexCountMax; - size_t sizeMax = fTexSizeMax; - - // take extraSize into account, but watch for underflow of size_t - if (extraSize > sizeMax) { - sizeMax = 0; - } else { - sizeMax -= extraSize; - } - - Entry* entry = fTail; - while (entry) { - if (fTexCount <= countMax && fTexSize <= sizeMax) { - break; - } - - Entry* prev = entry->fPrev; - // don't purge an entry that is locked - if (entry->isLocked()) { - entry = prev; - continue; - } - - fTexCount -= 1; - fTexSize -= entry->memSize(); - - // remove from our sorted and hash arrays - int index = this->findInSorted(entry->getKey()); - SkASSERT(index >= 0); - fSorted.remove(index); - index = entry->getKey().getHashIndex(); - if (entry == fHash[index]) { - fHash[index] = NULL; - } - - // now detach it from our llist - Entry* next = entry->fNext; - if (prev) { - prev->fNext = next; - } else { - fHead = next; - } - if (next) { - next->fPrev = prev; - } else { - fTail = prev; - } - - // now delete it -#ifdef TRACE_TEXTURE_CACHE_PURGE - SkDebugf("---- purge texture cache %d size=%d\n", - entry->name(), entry->memSize()); -#endif - SkDELETE(entry); - - // keep going - entry = prev; - } - - this->validate(); -} - -void SkTextureCache::setMaxCount(size_t count) { - if (fTexCountMax != count) { - fTexCountMax = count; - this->purgeIfNecessary(0); - } -} - -void SkTextureCache::setMaxSize(size_t size) { - if (fTexSizeMax != size) { - fTexSizeMax = size; - this->purgeIfNecessary(0); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -#ifdef SK_DEBUG -void SkTextureCache::validate() const { - if (0 == fTexCount) { - SkASSERT(0 == fTexSize); - SkASSERT(NULL == fHead); - SkASSERT(NULL == fTail); - return; - } - - SkASSERT(fTexSize); // do we allow a zero-sized texture? - SkASSERT(fHead); - SkASSERT(fTail); - - SkASSERT(NULL == fHead->fPrev); - SkASSERT(NULL == fTail->fNext); - if (1 == fTexCount) { - SkASSERT(fHead == fTail); - } - - const Entry* entry = fHead; - size_t count = 0; - size_t size = 0; - size_t i; - - while (entry != NULL) { - SkASSERT(count < fTexCount); - SkASSERT(size < fTexSize); - size += entry->memSize(); - count += 1; - if (NULL == entry->fNext) { - SkASSERT(fTail == entry); - } - entry = entry->fNext; - } - SkASSERT(count == fTexCount); - SkASSERT(size == fTexSize); - - count = 0; - size = 0; - entry = fTail; - while (entry != NULL) { - SkASSERT(count < fTexCount); - SkASSERT(size < fTexSize); - size += entry->memSize(); - count += 1; - if (NULL == entry->fPrev) { - SkASSERT(fHead == entry); - } - entry = entry->fPrev; - } - SkASSERT(count == fTexCount); - SkASSERT(size == fTexSize); - - SkASSERT(count == (size_t)fSorted.count()); - for (i = 1; i < count; i++) { - SkASSERT(fSorted[i-1]->getKey() < fSorted[i]->getKey()); - } - - for (i = 0; i < kHashCount; i++) { - if (fHash[i]) { - size_t index = fHash[i]->getKey().getHashIndex(); - SkASSERT(index == i); - index = fSorted.find(fHash[i]); - SkASSERT((size_t)index < count); - } - } -} -#endif - - diff --git a/src/gl/SkTextureCache.h b/src/gl/SkTextureCache.h deleted file mode 100644 index 0bc3091..0000000 --- a/src/gl/SkTextureCache.h +++ /dev/null @@ -1,161 +0,0 @@ -#ifndef SkTextureCache_DEFINED -#define SkTextureCache_DEFINED - -#include "SkBitmap.h" -#include "SkPoint.h" -#include "SkGL.h" -#include "SkTDArray.h" - -class SkTextureCache { -public: - SkTextureCache(size_t maxCount, size_t maxSize); - ~SkTextureCache(); - - size_t getMaxCount() { return fTexCountMax; } - size_t getMaxSize() { return fTexSizeMax; } - - void setMaxCount(size_t count); - void setMaxSize(size_t size); - - /** Deletes all the caches. Pass true if the texture IDs are still valid, - and if so, it will call glDeleteTextures. Pass false if the texture IDs - are invalid (e.g. the gl-context has changed), in which case they will - just be abandoned. - */ - void deleteAllCaches(bool texturesAreValid); - - static int HashMask() { return kHashMask; } - - class Key { - public: - Key(const SkBitmap& bm) { - fGenID = bm.getGenerationID(); - fOffset = bm.pixelRefOffset(); - fWH = (bm.width() << 16) | bm.height(); - this->computeHash(); - } - - int getHashIndex() const { return fHashIndex; } - - friend bool operator==(const Key& a, const Key& b) { - return a.fHash == b.fHash && - a.fGenID == b.fGenID && - a.fOffset == b.fOffset && - a.fWH == b.fWH; - } - - friend bool operator<(const Key& a, const Key& b) { - if (a.fHash < b.fHash) { - return true; - } else if (a.fHash > b.fHash) { - return false; - } - - if (a.fGenID < b.fGenID) { - return true; - } else if (a.fGenID > b.fGenID) { - return false; - } - - if (a.fOffset < b.fOffset) { - return true; - } else if (a.fOffset > b.fOffset) { - return false; - } - - return a.fWH < b.fWH; - } - - private: - void computeHash() { - uint32_t hash = fGenID ^ fOffset ^ fWH; - fHash = hash; - hash ^= hash >> 16; - fHashIndex = hash & SkTextureCache::HashMask(); - } - - uint32_t fHash; // computed from the other fields - uint32_t fGenID; - size_t fOffset; - uint32_t fWH; - // for indexing into the texturecache's fHash - int fHashIndex; - }; - - class Entry { - public: - GLuint name() const { return fName; } - SkPoint texSize() const { return fTexSize; } - size_t memSize() const { return fMemSize; } - const Key& getKey() const { return fKey; } - - // call this to clear the texture name, in case the context has changed - // in which case we should't reference or delete this texture in GL - void abandonTexture() { fName = 0; } - - private: - Entry(const SkBitmap& bitmap); - ~Entry(); - - int lockCount() const { return fLockCount; } - bool isLocked() const { return fLockCount > 0; } - - void lock() { fLockCount += 1; } - void unlock() { - SkASSERT(fLockCount > 0); - fLockCount -= 1; - } - - private: - GLuint fName; - SkPoint fTexSize; - Key fKey; - size_t fMemSize; - int fLockCount; - - Entry* fPrev; - Entry* fNext; - - friend class SkTextureCache; - }; - - Entry* lock(const SkBitmap&); - void unlock(Entry*); - -private: - void purgeIfNecessary(size_t extraSize); - -#ifdef SK_DEBUG - void validate() const; -#else - void validate() const {} -#endif - - Entry* fHead; - Entry* fTail; - - // limits for the cache - size_t fTexCountMax; - size_t fTexSizeMax; - - // current values for the cache - size_t fTexCount; - size_t fTexSize; - - enum { - kHashBits = 6, - kHashCount = 1 << kHashBits, - kHashMask = kHashCount - 1 - }; - mutable Entry* fHash[kHashCount]; - SkTDArray<Entry*> fSorted; - - /* If we find the key, return the entry and ignore index. If we don't, - return NULL and set index to the place to insert the entry in fSorted - */ - Entry* find(const Key&, int* index) const; - // returns index or <0 if not found. Does NOT update hash - int findInSorted(const Key& key) const; -}; - -#endif diff --git a/src/images/SkCreateRLEPixelRef.cpp b/src/images/SkCreateRLEPixelRef.cpp index 5756237..a3bf5bc 100644 --- a/src/images/SkCreateRLEPixelRef.cpp +++ b/src/images/SkCreateRLEPixelRef.cpp @@ -22,12 +22,12 @@ RLEPixelRef::RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable) : SkPixelRef(NULL) { fRLEPixels = rlep; // we now own this ptr fCTable = ctable; - ctable->safeRef(); + SkSafeRef(ctable); } RLEPixelRef::~RLEPixelRef() { SkDELETE(fRLEPixels); - fCTable->safeUnref(); + SkSafeUnref(fCTable); } void* RLEPixelRef::onLockPixels(SkColorTable** ct) { diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp index abebfbf..4a790d5 100644 --- a/src/images/SkImageDecoder.cpp +++ b/src/images/SkImageDecoder.cpp @@ -57,10 +57,10 @@ SkImageDecoder::SkImageDecoder() } SkImageDecoder::~SkImageDecoder() { - fPeeker->safeUnref(); - fChooser->safeUnref(); - fAllocator->safeUnref(); - fReporter->safeUnref(); + SkSafeUnref(fPeeker); + SkSafeUnref(fChooser); + SkSafeUnref(fAllocator); + SkSafeUnref(fReporter); } SkImageDecoder::Format SkImageDecoder::getFormat() const { diff --git a/src/images/SkImageDecoder_Factory.cpp b/src/images/SkImageDecoder_Factory.cpp index 4009f42..e5ff395 100644 --- a/src/images/SkImageDecoder_Factory.cpp +++ b/src/images/SkImageDecoder_Factory.cpp @@ -22,7 +22,9 @@ typedef SkTRegistry<SkImageDecoder*, SkStream*> DecodeReg; -template DecodeReg* DecodeReg::gHead; +// N.B. You can't use "DecodeReg::gHead here" due to complex C++ +// corner cases. +template DecodeReg* SkTRegistry<SkImageDecoder*, SkStream*>::gHead; #ifdef SK_ENABLE_LIBPNG extern SkImageDecoder* sk_libpng_dfactory(SkStream*); diff --git a/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp index 1a85a1d..7d2d416 100644 --- a/src/images/SkImageRef.cpp +++ b/src/images/SkImageRef.cpp @@ -40,16 +40,16 @@ SkImageRef::~SkImageRef() { #endif fStream->unref(); - fFactory->safeUnref(); + SkSafeUnref(fFactory); } bool SkImageRef::getInfo(SkBitmap* bitmap) { SkAutoMutexAcquire ac(gImageRefMutex); - + if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) { return false; } - + SkASSERT(SkBitmap::kNo_Config != fBitmap.config()); if (bitmap) { bitmap->setConfig(fBitmap.config(), fBitmap.width(), fBitmap.height()); @@ -87,7 +87,7 @@ bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { if (fErrorInDecoding) { return false; } - + /* As soon as we really know our config, we record it, so that on subsequent calls to the codec, we are sure we will always get the same result. @@ -95,7 +95,7 @@ bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) { if (SkBitmap::kNo_Config != fBitmap.config()) { fConfig = fBitmap.config(); } - + if (NULL != fBitmap.getPixels() || (SkBitmap::kNo_Config != fBitmap.config() && SkImageDecoder::kDecodeBounds_Mode == mode)) { diff --git a/src/opts/SkBitmapProcState_opts_SSE2.cpp b/src/opts/SkBitmapProcState_opts_SSE2.cpp new file mode 100644 index 0000000..a24c6a1 --- /dev/null +++ b/src/opts/SkBitmapProcState_opts_SSE2.cpp @@ -0,0 +1,242 @@ +/* + ** + ** Copyright 2009, 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 <emmintrin.h> +#include "SkBitmapProcState_opts_SSE2.h" +#include "SkUtils.h" + +void S32_opaque_D32_filter_DX_SSE2(const SkBitmapProcState& s, + const uint32_t* xy, + int count, uint32_t* colors) { + SkASSERT(count > 0 && colors != NULL); + SkASSERT(s.fDoFilter); + SkASSERT(s.fBitmap->config() == SkBitmap::kARGB_8888_Config); + SkASSERT(s.fAlphaScale == 256); + + const char* srcAddr = static_cast<const char*>(s.fBitmap->getPixels()); + unsigned rb = s.fBitmap->rowBytes(); + uint32_t XY = *xy++; + unsigned y0 = XY >> 14; + const uint32_t* row0 = reinterpret_cast<const uint32_t*>(srcAddr + (y0 >> 4) * rb); + const uint32_t* row1 = reinterpret_cast<const uint32_t*>(srcAddr + (XY & 0x3FFF) * rb); + unsigned subY = y0 & 0xF; + + // ( 0, 0, 0, 0, 0, 0, 0, 16) + __m128i sixteen = _mm_cvtsi32_si128(16); + + // ( 0, 0, 0, 0, 16, 16, 16, 16) + sixteen = _mm_shufflelo_epi16(sixteen, 0); + + // ( 0, 0, 0, 0, 0, 0, 0, y) + __m128i allY = _mm_cvtsi32_si128(subY); + + // ( 0, 0, 0, 0, y, y, y, y) + allY = _mm_shufflelo_epi16(allY, 0); + + // ( 0, 0, 0, 0, 16-y, 16-y, 16-y, 16-y) + __m128i negY = _mm_sub_epi16(sixteen, allY); + + // (16-y, 16-y, 16-y, 16-y, y, y, y, y) + allY = _mm_unpacklo_epi64(allY, negY); + + // (16, 16, 16, 16, 16, 16, 16, 16 ) + sixteen = _mm_shuffle_epi32(sixteen, 0); + + // ( 0, 0, 0, 0, 0, 0, 0, 0) + __m128i zero = _mm_setzero_si128(); + do { + uint32_t XX = *xy++; // x0:14 | 4 | x1:14 + unsigned x0 = XX >> 18; + unsigned x1 = XX & 0x3FFF; + + // (0, 0, 0, 0, 0, 0, 0, x) + __m128i allX = _mm_cvtsi32_si128((XX >> 14) & 0x0F); + + // (0, 0, 0, 0, x, x, x, x) + allX = _mm_shufflelo_epi16(allX, 0); + + // (x, x, x, x, x, x, x, x) + allX = _mm_shuffle_epi32(allX, 0); + + // (16-x, 16-x, 16-x, 16-x, 16-x, 16-x, 16-x) + __m128i negX = _mm_sub_epi16(sixteen, allX); + + // Load 4 samples (pixels). + __m128i a00 = _mm_cvtsi32_si128(row0[x0]); + __m128i a01 = _mm_cvtsi32_si128(row0[x1]); + __m128i a10 = _mm_cvtsi32_si128(row1[x0]); + __m128i a11 = _mm_cvtsi32_si128(row1[x1]); + + // (0, 0, a00, a10) + __m128i a00a10 = _mm_unpacklo_epi32(a10, a00); + + // Expand to 16 bits per component. + a00a10 = _mm_unpacklo_epi8(a00a10, zero); + + // ((a00 * (16-y)), (a10 * y)). + a00a10 = _mm_mullo_epi16(a00a10, allY); + + // (a00 * (16-y) * (16-x), a10 * y * (16-x)). + a00a10 = _mm_mullo_epi16(a00a10, negX); + + // (0, 0, a01, a10) + __m128i a01a11 = _mm_unpacklo_epi32(a11, a01); + + // Expand to 16 bits per component. + a01a11 = _mm_unpacklo_epi8(a01a11, zero); + + // (a01 * (16-y)), (a11 * y) + a01a11 = _mm_mullo_epi16(a01a11, allY); + + // (a01 * (16-y) * x), (a11 * y * x) + a01a11 = _mm_mullo_epi16(a01a11, allX); + + // (a00*w00 + a01*w01, a10*w10 + a11*w11) + __m128i sum = _mm_add_epi16(a00a10, a01a11); + + // (DC, a00*w00 + a01*w01) + __m128i shifted = _mm_shuffle_epi32(sum, 0xEE); + + // (DC, a00*w00 + a01*w01 + a10*w10 + a11*w11) + sum = _mm_add_epi16(sum, shifted); + + // Divide each 16 bit component by 256. + sum = _mm_srli_epi16(sum, 8); + + // Pack lower 4 16 bit values of sum into lower 4 bytes. + sum = _mm_packus_epi16(sum, zero); + + // Extract low int and store. + *colors++ = _mm_cvtsi128_si32(sum); + } while (--count > 0); +} + +void S32_alpha_D32_filter_DX_SSE2(const SkBitmapProcState& s, + const uint32_t* xy, + int count, uint32_t* colors) { + SkASSERT(count > 0 && colors != NULL); + SkASSERT(s.fDoFilter); + SkASSERT(s.fBitmap->config() == SkBitmap::kARGB_8888_Config); + SkASSERT(s.fAlphaScale < 256); + + const char* srcAddr = static_cast<const char*>(s.fBitmap->getPixels()); + unsigned rb = s.fBitmap->rowBytes(); + uint32_t XY = *xy++; + unsigned y0 = XY >> 14; + const uint32_t* row0 = reinterpret_cast<const uint32_t*>(srcAddr + (y0 >> 4) * rb); + const uint32_t* row1 = reinterpret_cast<const uint32_t*>(srcAddr + (XY & 0x3FFF) * rb); + unsigned subY = y0 & 0xF; + + // ( 0, 0, 0, 0, 0, 0, 0, 16) + __m128i sixteen = _mm_cvtsi32_si128(16); + + // ( 0, 0, 0, 0, 16, 16, 16, 16) + sixteen = _mm_shufflelo_epi16(sixteen, 0); + + // ( 0, 0, 0, 0, 0, 0, 0, y) + __m128i allY = _mm_cvtsi32_si128(subY); + + // ( 0, 0, 0, 0, y, y, y, y) + allY = _mm_shufflelo_epi16(allY, 0); + + // ( 0, 0, 0, 0, 16-y, 16-y, 16-y, 16-y) + __m128i negY = _mm_sub_epi16(sixteen, allY); + + // (16-y, 16-y, 16-y, 16-y, y, y, y, y) + allY = _mm_unpacklo_epi64(allY, negY); + + // (16, 16, 16, 16, 16, 16, 16, 16 ) + sixteen = _mm_shuffle_epi32(sixteen, 0); + + // ( 0, 0, 0, 0, 0, 0, 0, 0) + __m128i zero = _mm_setzero_si128(); + + // ( alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha ) + __m128i alpha = _mm_set1_epi16(s.fAlphaScale); + + do { + uint32_t XX = *xy++; // x0:14 | 4 | x1:14 + unsigned x0 = XX >> 18; + unsigned x1 = XX & 0x3FFF; + + // (0, 0, 0, 0, 0, 0, 0, x) + __m128i allX = _mm_cvtsi32_si128((XX >> 14) & 0x0F); + + // (0, 0, 0, 0, x, x, x, x) + allX = _mm_shufflelo_epi16(allX, 0); + + // (x, x, x, x, x, x, x, x) + allX = _mm_shuffle_epi32(allX, 0); + + // (16-x, 16-x, 16-x, 16-x, 16-x, 16-x, 16-x) + __m128i negX = _mm_sub_epi16(sixteen, allX); + + // Load 4 samples (pixels). + __m128i a00 = _mm_cvtsi32_si128(row0[x0]); + __m128i a01 = _mm_cvtsi32_si128(row0[x1]); + __m128i a10 = _mm_cvtsi32_si128(row1[x0]); + __m128i a11 = _mm_cvtsi32_si128(row1[x1]); + + // (0, 0, a00, a10) + __m128i a00a10 = _mm_unpacklo_epi32(a10, a00); + + // Expand to 16 bits per component. + a00a10 = _mm_unpacklo_epi8(a00a10, zero); + + // ((a00 * (16-y)), (a10 * y)). + a00a10 = _mm_mullo_epi16(a00a10, allY); + + // (a00 * (16-y) * (16-x), a10 * y * (16-x)). + a00a10 = _mm_mullo_epi16(a00a10, negX); + + // (0, 0, a01, a10) + __m128i a01a11 = _mm_unpacklo_epi32(a11, a01); + + // Expand to 16 bits per component. + a01a11 = _mm_unpacklo_epi8(a01a11, zero); + + // (a01 * (16-y)), (a11 * y) + a01a11 = _mm_mullo_epi16(a01a11, allY); + + // (a01 * (16-y) * x), (a11 * y * x) + a01a11 = _mm_mullo_epi16(a01a11, allX); + + // (a00*w00 + a01*w01, a10*w10 + a11*w11) + __m128i sum = _mm_add_epi16(a00a10, a01a11); + + // (DC, a00*w00 + a01*w01) + __m128i shifted = _mm_shuffle_epi32(sum, 0xEE); + + // (DC, a00*w00 + a01*w01 + a10*w10 + a11*w11) + sum = _mm_add_epi16(sum, shifted); + + // Divide each 16 bit component by 256. + sum = _mm_srli_epi16(sum, 8); + + // Multiply by alpha. + sum = _mm_mullo_epi16(sum, alpha); + + // Divide each 16 bit component by 256. + sum = _mm_srli_epi16(sum, 8); + + // Pack lower 4 16 bit values of sum into lower 4 bytes. + sum = _mm_packus_epi16(sum, zero); + + // Extract low int and store. + *colors++ = _mm_cvtsi128_si32(sum); + } while (--count > 0); +} diff --git a/src/opts/SkBitmapProcState_opts_SSE2.h b/src/opts/SkBitmapProcState_opts_SSE2.h new file mode 100644 index 0000000..11d305b --- /dev/null +++ b/src/opts/SkBitmapProcState_opts_SSE2.h @@ -0,0 +1,27 @@ +/* + ** + ** Copyright 2009, 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 "SkBitmapProcState.h" + +void S32_opaque_D32_filter_DX_SSE2(const SkBitmapProcState& s, + const uint32_t* xy, + int count, uint32_t* colors); +void S32_alpha_D32_filter_DX_SSE2(const SkBitmapProcState& s, + const uint32_t* xy, + int count, uint32_t* colors); +void Color32_SSE2(SkPMColor dst[], const SkPMColor src[], int count, + SkPMColor color); diff --git a/src/opts/SkBlitRow_opts_SSE2.cpp b/src/opts/SkBlitRow_opts_SSE2.cpp new file mode 100644 index 0000000..244dbb4 --- /dev/null +++ b/src/opts/SkBlitRow_opts_SSE2.cpp @@ -0,0 +1,392 @@ +/* + ** + ** Copyright 2009, 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 "SkBlitRow_opts_SSE2.h" +#include "SkColorPriv.h" +#include "SkUtils.h" + +#include <emmintrin.h> + +/* SSE2 version of S32_Blend_BlitRow32() + * portable version is in core/SkBlitRow_D32.cpp + */ +void S32_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha) { + SkASSERT(alpha <= 255); + if (count <= 0) { + return; + } + + uint32_t src_scale = SkAlpha255To256(alpha); + uint32_t dst_scale = 256 - src_scale; + + if (count >= 4) { + SkASSERT(((size_t)dst & 0x03) == 0); + while (((size_t)dst & 0x0F) != 0) { + *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale); + src++; + dst++; + count--; + } + + const __m128i *s = reinterpret_cast<const __m128i*>(src); + __m128i *d = reinterpret_cast<__m128i*>(dst); + __m128i rb_mask = _mm_set1_epi32(0x00FF00FF); + __m128i src_scale_wide = _mm_set1_epi16(src_scale); + __m128i dst_scale_wide = _mm_set1_epi16(dst_scale); + while (count >= 4) { + // Load 4 pixels each of src and dest. + __m128i src_pixel = _mm_loadu_si128(s); + __m128i dst_pixel = _mm_load_si128(d); + + // Get red and blue pixels into lower byte of each word. + __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel); + __m128i src_rb = _mm_and_si128(rb_mask, src_pixel); + + // Get alpha and green into lower byte of each word. + __m128i dst_ag = _mm_srli_epi16(dst_pixel, 8); + __m128i src_ag = _mm_srli_epi16(src_pixel, 8); + + // Multiply by scale. + src_rb = _mm_mullo_epi16(src_rb, src_scale_wide); + src_ag = _mm_mullo_epi16(src_ag, src_scale_wide); + dst_rb = _mm_mullo_epi16(dst_rb, dst_scale_wide); + dst_ag = _mm_mullo_epi16(dst_ag, dst_scale_wide); + + // Divide by 256. + src_rb = _mm_srli_epi16(src_rb, 8); + dst_rb = _mm_srli_epi16(dst_rb, 8); + src_ag = _mm_andnot_si128(rb_mask, src_ag); + dst_ag = _mm_andnot_si128(rb_mask, dst_ag); + + // Combine back into RGBA. + src_pixel = _mm_or_si128(src_rb, src_ag); + dst_pixel = _mm_or_si128(dst_rb, dst_ag); + + // Add result + __m128i result = _mm_add_epi8(src_pixel, dst_pixel); + _mm_store_si128(d, result); + s++; + d++; + count -= 4; + } + src = reinterpret_cast<const SkPMColor*>(s); + dst = reinterpret_cast<SkPMColor*>(d); + } + + while (count > 0) { + *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale); + src++; + dst++; + count--; + } +} + +void S32A_Opaque_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha) { + SkASSERT(alpha == 255); + if (count <= 0) { + return; + } + + if (count >= 4) { + SkASSERT(((size_t)dst & 0x03) == 0); + while (((size_t)dst & 0x0F) != 0) { + *dst = SkPMSrcOver(*src, *dst); + src++; + dst++; + count--; + } + + const __m128i *s = reinterpret_cast<const __m128i*>(src); + __m128i *d = reinterpret_cast<__m128i*>(dst); +#ifdef SK_USE_ACCURATE_BLENDING + __m128i rb_mask = _mm_set1_epi32(0x00FF00FF); + __m128i c_128 = _mm_set1_epi16(128); // 8 copies of 128 (16-bit) + __m128i c_255 = _mm_set1_epi16(255); // 8 copies of 255 (16-bit) + while (count >= 4) { + // Load 4 pixels + __m128i src_pixel = _mm_loadu_si128(s); + __m128i dst_pixel = _mm_load_si128(d); + + __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel); + __m128i dst_ag = _mm_srli_epi16(dst_pixel, 8); + // Shift alphas down to lower 8 bits of each quad. + __m128i alpha = _mm_srli_epi32(src_pixel, 24); + + // Copy alpha to upper 3rd byte of each quad + alpha = _mm_or_si128(alpha, _mm_slli_epi32(alpha, 16)); + + // Subtract alphas from 255, to get 0..255 + alpha = _mm_sub_epi16(c_255, alpha); + + // Multiply by red and blue by src alpha. + dst_rb = _mm_mullo_epi16(dst_rb, alpha); + // Multiply by alpha and green by src alpha. + dst_ag = _mm_mullo_epi16(dst_ag, alpha); + + // dst_rb_low = (dst_rb >> 8) + __m128i dst_rb_low = _mm_srli_epi16(dst_rb, 8); + __m128i dst_ag_low = _mm_srli_epi16(dst_ag, 8); + + // dst_rb = (dst_rb + dst_rb_low + 128) >> 8 + dst_rb = _mm_add_epi16(dst_rb, dst_rb_low); + dst_rb = _mm_add_epi16(dst_rb, c_128); + dst_rb = _mm_srli_epi16(dst_rb, 8); + + // dst_ag = (dst_ag + dst_ag_low + 128) & ag_mask + dst_ag = _mm_add_epi16(dst_ag, dst_ag_low); + dst_ag = _mm_add_epi16(dst_ag, c_128); + dst_ag = _mm_andnot_si128(rb_mask, dst_ag); + + // Combine back into RGBA. + dst_pixel = _mm_or_si128(dst_rb, dst_ag); + + // Add result + __m128i result = _mm_add_epi8(src_pixel, dst_pixel); + _mm_store_si128(d, result); + s++; + d++; + count -= 4; + } + #else + __m128i rb_mask = _mm_set1_epi32(0x00FF00FF); + __m128i c_256 = _mm_set1_epi16(0x0100); // 8 copies of 256 (16-bit) + while (count >= 4) { + // Load 4 pixels + __m128i src_pixel = _mm_loadu_si128(s); + __m128i dst_pixel = _mm_load_si128(d); + + __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel); + __m128i dst_ag = _mm_srli_epi16(dst_pixel, 8); + + // (a0, g0, a1, g1, a2, g2, a3, g3) (low byte of each word) + __m128i alpha = _mm_srli_epi16(src_pixel, 8); + + // (a0, a0, a1, a1, a2, g2, a3, g3) + alpha = _mm_shufflehi_epi16(alpha, 0xF5); + + // (a0, a0, a1, a1, a2, a2, a3, a3) + alpha = _mm_shufflelo_epi16(alpha, 0xF5); + + // Subtract alphas from 256, to get 1..256 + alpha = _mm_sub_epi16(c_256, alpha); + + // Multiply by red and blue by src alpha. + dst_rb = _mm_mullo_epi16(dst_rb, alpha); + // Multiply by alpha and green by src alpha. + dst_ag = _mm_mullo_epi16(dst_ag, alpha); + + // Divide by 256. + dst_rb = _mm_srli_epi16(dst_rb, 8); + + // Mask out high bits (already in the right place) + dst_ag = _mm_andnot_si128(rb_mask, dst_ag); + + // Combine back into RGBA. + dst_pixel = _mm_or_si128(dst_rb, dst_ag); + + // Add result + __m128i result = _mm_add_epi8(src_pixel, dst_pixel); + _mm_store_si128(d, result); + s++; + d++; + count -= 4; + } +#endif + src = reinterpret_cast<const SkPMColor*>(s); + dst = reinterpret_cast<SkPMColor*>(d); + } + + while (count > 0) { + *dst = SkPMSrcOver(*src, *dst); + src++; + dst++; + count--; + } +} + +void S32A_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha) { + SkASSERT(alpha <= 255); + if (count <= 0) { + return; + } + + if (count >= 4) { + while (((size_t)dst & 0x0F) != 0) { + *dst = SkBlendARGB32(*src, *dst, alpha); + src++; + dst++; + count--; + } + + uint32_t src_scale = SkAlpha255To256(alpha); + + const __m128i *s = reinterpret_cast<const __m128i*>(src); + __m128i *d = reinterpret_cast<__m128i*>(dst); + __m128i src_scale_wide = _mm_set1_epi16(src_scale); + __m128i rb_mask = _mm_set1_epi32(0x00FF00FF); + __m128i c_256 = _mm_set1_epi16(256); // 8 copies of 256 (16-bit) + while (count >= 4) { + // Load 4 pixels each of src and dest. + __m128i src_pixel = _mm_loadu_si128(s); + __m128i dst_pixel = _mm_load_si128(d); + + // Get red and blue pixels into lower byte of each word. + __m128i dst_rb = _mm_and_si128(rb_mask, dst_pixel); + __m128i src_rb = _mm_and_si128(rb_mask, src_pixel); + + // Get alpha and green into lower byte of each word. + __m128i dst_ag = _mm_srli_epi16(dst_pixel, 8); + __m128i src_ag = _mm_srli_epi16(src_pixel, 8); + + // Put per-pixel alpha in low byte of each word. + __m128i dst_alpha = _mm_shufflehi_epi16(src_ag, 0xF5); + dst_alpha = _mm_shufflelo_epi16(dst_alpha, 0xF5); + + // dst_alpha = dst_alpha * src_scale + dst_alpha = _mm_mullo_epi16(dst_alpha, src_scale_wide); + + // Divide by 256. + dst_alpha = _mm_srli_epi16(dst_alpha, 8); + + // Subtract alphas from 256, to get 1..256 + dst_alpha = _mm_sub_epi16(c_256, dst_alpha); + + // Multiply red and blue by dst pixel alpha. + dst_rb = _mm_mullo_epi16(dst_rb, dst_alpha); + // Multiply alpha and green by dst pixel alpha. + dst_ag = _mm_mullo_epi16(dst_ag, dst_alpha); + + // Multiply red and blue by global alpha. + src_rb = _mm_mullo_epi16(src_rb, src_scale_wide); + // Multiply alpha and green by global alpha. + src_ag = _mm_mullo_epi16(src_ag, src_scale_wide); + + // Divide by 256. + dst_rb = _mm_srli_epi16(dst_rb, 8); + src_rb = _mm_srli_epi16(src_rb, 8); + + // Mask out low bits (goodies already in the right place; no need to divide) + dst_ag = _mm_andnot_si128(rb_mask, dst_ag); + src_ag = _mm_andnot_si128(rb_mask, src_ag); + + // Combine back into RGBA. + dst_pixel = _mm_or_si128(dst_rb, dst_ag); + src_pixel = _mm_or_si128(src_rb, src_ag); + + // Add two pixels into result. + __m128i result = _mm_add_epi8(src_pixel, dst_pixel); + _mm_store_si128(d, result); + s++; + d++; + count -= 4; + } + src = reinterpret_cast<const SkPMColor*>(s); + dst = reinterpret_cast<SkPMColor*>(d); + } + + while (count > 0) { + *dst = SkBlendARGB32(*src, *dst, alpha); + src++; + dst++; + count--; + } +} + +/* SSE2 version of Color32() + * portable version is in core/SkBlitRow_D32.cpp + */ +void Color32_SSE2(SkPMColor dst[], const SkPMColor src[], int count, + SkPMColor color) { + + if (count <= 0) { + return; + } + + if (0 == color) { + if (src != dst) { + memcpy(dst, src, count * sizeof(SkPMColor)); + } + } + + unsigned colorA = SkGetPackedA32(color); + if (255 == colorA) { + sk_memset32(dst, color, count); + } else { + unsigned scale = 256 - SkAlpha255To256(colorA); + + if (count >= 4) { + SkASSERT(((size_t)dst & 0x03) == 0); + while (((size_t)dst & 0x0F) != 0) { + *dst = color + SkAlphaMulQ(*src, scale); + src++; + dst++; + count--; + } + + const __m128i *s = reinterpret_cast<const __m128i*>(src); + __m128i *d = reinterpret_cast<__m128i*>(dst); + __m128i rb_mask = _mm_set1_epi32(0x00FF00FF); + __m128i src_scale_wide = _mm_set1_epi16(scale); + __m128i color_wide = _mm_set1_epi32(color); + while (count >= 4) { + // Load 4 pixels each of src and dest. + __m128i src_pixel = _mm_loadu_si128(s); + + // Get red and blue pixels into lower byte of each word. + __m128i src_rb = _mm_and_si128(rb_mask, src_pixel); + + // Get alpha and green into lower byte of each word. + __m128i src_ag = _mm_srli_epi16(src_pixel, 8); + + // Multiply by scale. + src_rb = _mm_mullo_epi16(src_rb, src_scale_wide); + src_ag = _mm_mullo_epi16(src_ag, src_scale_wide); + + // Divide by 256. + src_rb = _mm_srli_epi16(src_rb, 8); + src_ag = _mm_andnot_si128(rb_mask, src_ag); + + // Combine back into RGBA. + src_pixel = _mm_or_si128(src_rb, src_ag); + + // Add color to result. + __m128i result = _mm_add_epi8(color_wide, src_pixel); + + // Store result. + _mm_store_si128(d, result); + s++; + d++; + count -= 4; + } + src = reinterpret_cast<const SkPMColor*>(s); + dst = reinterpret_cast<SkPMColor*>(d); + } + + while (count > 0) { + *dst = color + SkAlphaMulQ(*src, scale); + src += 1; + dst += 1; + count--; + } + } +} diff --git a/src/opts/SkBlitRow_opts_SSE2.h b/src/opts/SkBlitRow_opts_SSE2.h new file mode 100644 index 0000000..c22edd8 --- /dev/null +++ b/src/opts/SkBlitRow_opts_SSE2.h @@ -0,0 +1,30 @@ +/* + ** + ** Copyright 2009, 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 "SkBlitRow.h" + +void S32_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha); + +void S32A_Opaque_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha); + +void S32A_Blend_BlitRow32_SSE2(SkPMColor* SK_RESTRICT dst, + const SkPMColor* SK_RESTRICT src, + int count, U8CPU alpha); diff --git a/src/opts/SkBlitRow_opts_arm.cpp b/src/opts/SkBlitRow_opts_arm.cpp index 9fa3e74..2ad7ba7 100644 --- a/src/opts/SkBlitRow_opts_arm.cpp +++ b/src/opts/SkBlitRow_opts_arm.cpp @@ -1083,3 +1083,6 @@ SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) { return platform_32_procs[flags]; } +SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() { + return NULL; +} diff --git a/src/opts/SkBlitRow_opts_none.cpp b/src/opts/SkBlitRow_opts_none.cpp index 8e0cddc..0eb1185 100644 --- a/src/opts/SkBlitRow_opts_none.cpp +++ b/src/opts/SkBlitRow_opts_none.cpp @@ -13,3 +13,7 @@ SkBlitRow::Proc SkBlitRow::PlatformProcs565(unsigned flags) { SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) { return NULL; } + +SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() { + return NULL; +} diff --git a/src/opts/SkUtils_opts_SSE2.cpp b/src/opts/SkUtils_opts_SSE2.cpp new file mode 100644 index 0000000..0537033 --- /dev/null +++ b/src/opts/SkUtils_opts_SSE2.cpp @@ -0,0 +1,77 @@ +/* + ** + ** Copyright 2009, 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 <emmintrin.h> +#include "SkUtils_opts_SSE2.h" + +void sk_memset16_SSE2(uint16_t *dst, uint16_t value, int count) +{ + SkASSERT(dst != NULL && count >= 0); + + // dst must be 2-byte aligned. + SkASSERT((((size_t) dst) & 0x01) == 0); + + if (count >= 32) { + while (((size_t)dst) & 0x0F) { + *dst++ = value; + --count; + } + __m128i *d = reinterpret_cast<__m128i*>(dst); + __m128i value_wide = _mm_set1_epi16(value); + while (count >= 32) { + _mm_store_si128(d++, value_wide); + _mm_store_si128(d++, value_wide); + _mm_store_si128(d++, value_wide); + _mm_store_si128(d++, value_wide); + count -= 32; + } + dst = reinterpret_cast<uint16_t*>(d); + } + while (count > 0) { + *dst++ = value; + --count; + } +} + +void sk_memset32_SSE2(uint32_t *dst, uint32_t value, int count) +{ + SkASSERT(dst != NULL && count >= 0); + + // dst must be 4-byte aligned. + SkASSERT((((size_t) dst) & 0x03) == 0); + + if (count >= 16) { + while (((size_t)dst) & 0x0F) { + *dst++ = value; + --count; + } + __m128i *d = reinterpret_cast<__m128i*>(dst); + __m128i value_wide = _mm_set1_epi32(value); + while (count >= 16) { + _mm_store_si128(d++, value_wide); + _mm_store_si128(d++, value_wide); + _mm_store_si128(d++, value_wide); + _mm_store_si128(d++, value_wide); + count -= 16; + } + dst = reinterpret_cast<uint32_t*>(d); + } + while (count > 0) { + *dst++ = value; + --count; + } +} diff --git a/src/opts/SkUtils_opts_SSE2.h b/src/opts/SkUtils_opts_SSE2.h new file mode 100644 index 0000000..a54e82f --- /dev/null +++ b/src/opts/SkUtils_opts_SSE2.h @@ -0,0 +1,21 @@ +/* + ** + ** Copyright 2009, 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 "SkTypes.h" + +void sk_memset16_SSE2(uint16_t *dst, uint16_t value, int count); +void sk_memset32_SSE2(uint32_t *dst, uint32_t value, int count); diff --git a/src/opts/SkUtils_opts_none.cpp b/src/opts/SkUtils_opts_none.cpp new file mode 100644 index 0000000..108ce9c --- /dev/null +++ b/src/opts/SkUtils_opts_none.cpp @@ -0,0 +1,26 @@ +/* + ** + ** Copyright 2009, 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 "SkUtils.h" + +SkMemset16Proc SkMemset16GetPlatformProc() { + return NULL; +} + +SkMemset32Proc SkMemset32GetPlatformProc() { + return NULL; +} diff --git a/src/opts/memset.arm.S b/src/opts/memset.arm.S new file mode 100644 index 0000000..04a7027 --- /dev/null +++ b/src/opts/memset.arm.S @@ -0,0 +1,110 @@ +/* + * 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. + */ + +/* Changes: + * 2010-08-11 Steve McIntyre <steve.mcintyre@arm.com> + * Added small changes to the two functions to make them work on the + * specified number of 16- or 32-bit values rather than the original + * code which was specified as a count of bytes. More verbose comments + * to aid future maintenance. + */ + + .text + .align + + .global arm_memset32 + .type arm_memset32, %function + .global arm_memset16 + .type arm_memset16, %function + +/* + * Optimized memset functions for ARM. + * + * void arm_memset16(uint16_t* dst, uint16_t value, int count); + * void arm_memset32(uint32_t* dst, uint32_t value, int count); + * + */ +arm_memset16: + .fnstart + push {lr} + + /* expand the data to 32 bits */ + orr r1, r1, lsl #16 + + /* align to 32 bits */ + tst r0, #2 + strneh r1, [r0], #2 + subne r2, r2, #2 + + /* Multiply count by 2 - go from the number of 16-bit shorts + * to the number of bytes desired. */ + mov r2, r2, lsl #1 + + /* Now jump into the main loop below. */ + b .Lwork_32 + .fnend + +arm_memset32: + .fnstart + push {lr} + + /* Multiply count by 4 - go from the number of 32-bit words to + * the number of bytes desired. */ + mov r2, r2, lsl #2 + +.Lwork_32: + /* Set up registers ready for writing them out. */ + mov ip, r1 + mov lr, r1 + + /* Try to align the destination to a cache line. Assume 32 + * byte (8 word) cache lines, it's the common case. */ + rsb r3, r0, #0 + ands r3, r3, #0x1C + beq .Laligned32 + cmp r3, r2 + andhi r3, r2, #0x1C + sub r2, r2, r3 + + /* (Optionally) write any unaligned leading bytes. + * (0-28 bytes, length in r3) */ + movs r3, r3, lsl #28 + stmcsia r0!, {r1, lr} + stmcsia r0!, {r1, lr} + stmmiia r0!, {r1, lr} + movs r3, r3, lsl #2 + strcs r1, [r0], #4 + + /* Now quickly loop through the cache-aligned data. */ +.Laligned32: + mov r3, r1 +1: subs r2, r2, #32 + stmhsia r0!, {r1,r3,ip,lr} + stmhsia r0!, {r1,r3,ip,lr} + bhs 1b + add r2, r2, #32 + + /* (Optionally) store any remaining trailing bytes. + * (0-30 bytes, length in r2) */ + movs r2, r2, lsl #28 + stmcsia r0!, {r1,r3,ip,lr} + stmmiia r0!, {r1,lr} + movs r2, r2, lsl #2 + strcs r1, [r0], #4 + strmih lr, [r0], #2 + + pop {pc} + .fnend diff --git a/src/opts/memset16_neon.S b/src/opts/memset16_neon.S new file mode 100644 index 0000000..b47cc22 --- /dev/null +++ b/src/opts/memset16_neon.S @@ -0,0 +1,152 @@ +/*************************************************************************** + Copyright (c) 2009,2010, Code Aurora Forum. All rights reserved. + + 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. + ***************************************************************************/ + +/*************************************************************************** + Neon memset: Attempts to do a memset with Neon registers if possible, + Inputs: + s: The buffer to write to + c: The integer data to write to the buffer + n: The size_t count. + Outputs: + +***************************************************************************/ + + .code 32 + .fpu neon + .align 4 + .globl memset16_neon + .func + +memset16_neon: + cmp r2, #0 + bxeq lr + + /* Keep in mind that r2 -- the count argument -- is for the + * number of 16-bit items to copy. + */ + lsl r2, r2, #1 + + push {r0} + + /* If we have < 8 bytes, just do a quick loop to handle that */ + cmp r2, #8 + bgt memset_gt4 +memset_smallcopy_loop: + strh r1, [r0], #2 + subs r2, r2, #2 + bne memset_smallcopy_loop +memset_smallcopy_done: + pop {r0} + bx lr + +memset_gt4: + /* + * Duplicate the r1 lowest 16-bits across r1. The idea is to have + * a register with two 16-bit-values we can copy. We do this by + * duplicating lowest 16-bits of r1 to upper 16-bits. + */ + orr r1, r1, r1, lsl #16 + /* + * If we're copying > 64 bytes, then we may want to get + * onto a 16-byte boundary to improve speed even more. + */ + cmp r2, #64 + blt memset_route + ands r12, r0, #0xf + beq memset_route + /* + * Determine the number of bytes to move forward to get to the 16-byte + * boundary. Note that this will be a multiple of 4, since we + * already are word-aligned. + */ + rsb r12, r12, #16 + sub r2, r2, r12 + lsls r12, r12, #29 + strmi r1, [r0], #4 + strcs r1, [r0], #4 + strcs r1, [r0], #4 + lsls r12, r12, #2 + strcsh r1, [r0], #2 +memset_route: + /* + * Decide where to route for the maximum copy sizes. Note that we + * build q0 and q1 depending on if we'll need it, so that's + * interwoven here as well. + */ + vdup.u32 d0, r1 + cmp r2, #16 + blt memset_8 + vmov d1, d0 + cmp r2, #64 + blt memset_16 + vmov q1, q0 + cmp r2, #128 + blt memset_32 +memset_128: + mov r12, r2, lsr #7 +memset_128_loop: + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + subs r12, r12, #1 + bne memset_128_loop + ands r2, r2, #0x7f + beq memset_end +memset_32: + movs r12, r2, lsr #5 + beq memset_16 +memset_32_loop: + subs r12, r12, #1 + vst1.64 {q0, q1}, [r0]! + bne memset_32_loop + ands r2, r2, #0x1f + beq memset_end +memset_16: + movs r12, r2, lsr #4 + beq memset_8 +memset_16_loop: + subs r12, r12, #1 + vst1.32 {q0}, [r0]! + bne memset_16_loop + ands r2, r2, #0xf + beq memset_end + /* + * memset_8 isn't a loop, since we try to do our loops at 16 + * bytes and above. We should loop there, then drop down here + * to finish the <16-byte versions. Same for memset_4 and + * memset_1. + */ +memset_8: + cmp r2, #8 + blt memset_4 + subs r2, r2, #8 + vst1.32 {d0}, [r0]! +memset_4: + cmp r2, #4 + blt memset_2 + subs r2, r2, #4 + str r1, [r0], #4 +memset_2: + cmp r2, #0 + ble memset_end + strh r1, [r0], #2 +memset_end: + pop {r0} + bx lr + + .endfunc + .end diff --git a/src/opts/memset32_neon.S b/src/opts/memset32_neon.S new file mode 100644 index 0000000..9052c4f --- /dev/null +++ b/src/opts/memset32_neon.S @@ -0,0 +1,122 @@ +/*************************************************************************** + Copyright (c) 2009,2010, Code Aurora Forum. All rights reserved. + + 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. + ***************************************************************************/ + + .code 32 + .fpu neon + .align 4 + .globl memset32_neon + .func + + /* r0 = buffer, r1 = value, r2 = times to write */ +memset32_neon: + cmp r2, #1 + streq r1, [r0], #4 + bxeq lr + + cmp r2, #4 + bgt memset32_neon_start + cmp r2, #0 + bxeq lr +memset32_neon_small: + str r1, [r0], #4 + subs r2, r2, #1 + bne memset32_neon_small + bx lr +memset32_neon_start: + cmp r2, #16 + blt memset32_dropthru + vdup.32 q0, r1 + vmov q1, q0 + cmp r2, #32 + blt memset32_16 + cmp r2, #64 + blt memset32_32 + cmp r2, #128 + blt memset32_64 +memset32_128: + movs r12, r2, lsr #7 +memset32_loop128: + subs r12, r12, #1 + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + bne memset32_loop128 + ands r2, r2, #0x7f + bxeq lr +memset32_64: + movs r12, r2, lsr #6 + beq memset32_32 + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + ands r2, r2, #0x3f + bxeq lr +memset32_32: + movs r12, r2, lsr #5 + beq memset32_16 + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! + ands r2, r2, #0x1f + bxeq lr +memset32_16: + movs r12, r2, lsr #4 + beq memset32_dropthru + and r2, r2, #0xf + vst1.64 {q0, q1}, [r0]! + vst1.64 {q0, q1}, [r0]! +memset32_dropthru: + rsb r2, r2, #15 + add pc, pc, r2, lsl #2 + nop + str r1, [r0, #56] + str r1, [r0, #52] + str r1, [r0, #48] + str r1, [r0, #44] + str r1, [r0, #40] + str r1, [r0, #36] + str r1, [r0, #32] + str r1, [r0, #28] + str r1, [r0, #24] + str r1, [r0, #20] + str r1, [r0, #16] + str r1, [r0, #12] + str r1, [r0, #8] + str r1, [r0, #4] + str r1, [r0, #0] + bx lr + + .endfunc + .end diff --git a/src/opts/opts_check_SSE2.cpp b/src/opts/opts_check_SSE2.cpp new file mode 100644 index 0000000..fa7b17a --- /dev/null +++ b/src/opts/opts_check_SSE2.cpp @@ -0,0 +1,122 @@ +/* + ** + ** Copyright 2009, 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 "SkBitmapProcState_opts_SSE2.h" +#include "SkBlitRow_opts_SSE2.h" +#include "SkUtils_opts_SSE2.h" +#include "SkUtils.h" + +/* This file must *not* be compiled with -msse or -msse2, otherwise + gcc may generate sse2 even for scalar ops (and thus give an invalid + instruction on Pentium3 on the code below). Only files named *_SSE2.cpp + in this directory should be compiled with -msse2. */ + +#if defined(__x86_64__) || defined(_WIN64) +/* All x86_64 machines have SSE2, so don't even bother checking. */ +static inline bool hasSSE2() { + return true; +} +#else +#ifdef _MSC_VER +static inline void getcpuid(int info_type, int info[4]) { + __asm { + mov eax, [info_type] + cpuid + mov edi, [info] + mov [edi], eax + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], edx + } +} +#else +static inline void getcpuid(int info_type, int info[4]) { + // We save and restore ebx, so this code can be compatible with -fPIC + asm volatile ( + "pushl %%ebx \n\t" + "cpuid \n\t" + "movl %%ebx, %1 \n\t" + "popl %%ebx \n\t" + : "=a"(info[0]), "=r"(info[1]), "=c"(info[2]), "=d"(info[3]) + : "a"(info_type) + ); +} +#endif + +static inline bool hasSSE2() { + int cpu_info[4] = { 0 }; + getcpuid(1, cpu_info); + return (cpu_info[3] & (1<<26)) != 0; +} +#endif + +void SkBitmapProcState::platformProcs() { + if (hasSSE2()) { + if (fSampleProc32 == S32_opaque_D32_filter_DX) { + fSampleProc32 = S32_opaque_D32_filter_DX_SSE2; + } else if (fSampleProc32 == S32_alpha_D32_filter_DX) { + fSampleProc32 = S32_alpha_D32_filter_DX_SSE2; + } + } +} + +static SkBlitRow::Proc32 platform_32_procs[] = { + NULL, // S32_Opaque, + S32_Blend_BlitRow32_SSE2, // S32_Blend, + S32A_Opaque_BlitRow32_SSE2, // S32A_Opaque + S32A_Blend_BlitRow32_SSE2, // S32A_Blend, +}; + +SkBlitRow::Proc SkBlitRow::PlatformProcs4444(unsigned flags) { + return NULL; +} + +SkBlitRow::Proc SkBlitRow::PlatformProcs565(unsigned flags) { + return NULL; +} + +SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() { + if (hasSSE2()) { + return Color32_SSE2; + } else { + return NULL; + } +} + +SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) { + if (hasSSE2()) { + return platform_32_procs[flags]; + } else { + return NULL; + } +} + +SkMemset16Proc SkMemset16GetPlatformProc() { + if (hasSSE2()) { + return sk_memset16_SSE2; + } else { + return NULL; + } +} + +SkMemset32Proc SkMemset32GetPlatformProc() { + if (hasSSE2()) { + return sk_memset32_SSE2; + } else { + return NULL; + } +} diff --git a/src/opts/opts_check_arm.cpp b/src/opts/opts_check_arm.cpp new file mode 100644 index 0000000..079e80f --- /dev/null +++ b/src/opts/opts_check_arm.cpp @@ -0,0 +1,31 @@ +/* + ** + ** Copyright 2006-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 "SkUtils.h" + +extern "C" { + void arm_memset16(uint16_t* dst, uint16_t value, int count); + void arm_memset32(uint32_t* dst, uint32_t value, int count); +} + +SkMemset16Proc SkMemset16GetPlatformProc() { + return arm_memset16; +} + +SkMemset32Proc SkMemset32GetPlatformProc() { + return arm_memset32; +} diff --git a/src/opts/opts_check_arm_neon.cpp b/src/opts/opts_check_arm_neon.cpp new file mode 100644 index 0000000..8f18df2 --- /dev/null +++ b/src/opts/opts_check_arm_neon.cpp @@ -0,0 +1,45 @@ +/*************************************************************************** + Copyright (c) 2010, Code Aurora Forum. All rights reserved. + + 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 "SkUtils.h" + +extern "C" void memset16_neon(uint16_t dst[], uint16_t value, int count); +extern "C" void memset32_neon(uint32_t dst[], uint32_t value, int count); + +static inline bool hasNeonRegisters() { +#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN) + return true; +#else + return false; +#endif +} + +SkMemset16Proc SkMemset16GetPlatformProc() { + if (hasNeonRegisters()) { + return memset16_neon; + } else { + return NULL; + } +} + +SkMemset32Proc SkMemset32GetPlatformProc() { + if (hasNeonRegisters()) { + return memset32_neon; + } else { + return NULL; + } +} diff --git a/src/opts/opts_sse2_files.mk b/src/opts/opts_sse2_files.mk new file mode 100644 index 0000000..822543d --- /dev/null +++ b/src/opts/opts_sse2_files.mk @@ -0,0 +1,5 @@ +SOURCE := \ + SkBlitRow_opts_SSE2.cpp \ + SkBitmapProcState_opts_SSE2.cpp \ + SkUtils_opts_SSE2.cpp \ + opts_check_SSE2.cpp diff --git a/src/ports/SkDebug_brew.cpp b/src/ports/SkDebug_brew.cpp new file mode 100644 index 0000000..03f7968 --- /dev/null +++ b/src/ports/SkDebug_brew.cpp @@ -0,0 +1,37 @@ +/* libs/corecg/SkDebug_brew.cpp +** +** Copyright 2009, The Android Open Source Project +** Copyright 2009, Company 100, 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 "SkTypes.h" + +#ifdef SK_BUILD_FOR_BREW + +static const size_t kBufferSize = 256; + +#include <AEEStdLib.h> +#include <stdarg.h> + +void SkDebugf(const char format[], ...) { + char buffer[kBufferSize + 1]; + va_list args; + va_start(args, format); + VSNPRINTF(buffer, kBufferSize, format, args); + va_end(args); + DBGPRINTF(buffer); +} + +#endif SK_BUILD_FOR_BREW diff --git a/src/ports/SkFontHost_FONTPATH.cpp b/src/ports/SkFontHost_FONTPATH.cpp new file mode 100644 index 0000000..98f4ba5 --- /dev/null +++ b/src/ports/SkFontHost_FONTPATH.cpp @@ -0,0 +1,338 @@ +/* libs/graphics/ports/SkFontHost_android.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 "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkString.h" +#include "SkStream.h" +#include <stdio.h> + +/* define this if we can use mmap() to access fonts from the filesystem */ +#define SK_CAN_USE_MMAP + +#ifndef SK_FONTPATH + #define SK_FONTPATH "the complete path for a font file" +#endif + +struct FontFaceRec { + const char* fFileName; + uint8_t fFamilyIndex; + SkBool8 fBold; + SkBool8 fItalic; + + static const FontFaceRec& FindFace(const FontFaceRec rec[], int count, + int isBold, int isItalic); +}; + +struct FontFamilyRec { + const FontFaceRec* fFaces; + int fFaceCount; +}; + +const FontFaceRec& FontFaceRec::FindFace(const FontFaceRec rec[], int count, + int isBold, int isItalic) +{ + SkASSERT(count > 0); + + int i; + + // look for an exact match + for (i = 0; i < count; i++) { + if (rec[i].fBold == isBold && rec[i].fItalic == isItalic) + return rec[i]; + } + // look for a match in the bold field + for (i = 0; i < count; i++) { + if (rec[i].fBold == isBold) + return rec[i]; + } + // look for a normal/regular face + for (i = 0; i < count; i++) { + if (!rec[i].fBold && !rec[i].fItalic) + return rec[i]; + } + // give up + return rec[0]; +} + +enum { + DEFAULT_FAMILY_INDEX, + + FAMILY_INDEX_COUNT +}; + +static const FontFaceRec gDefaultFaces[] = { + { SK_FONTPATH, DEFAULT_FAMILY_INDEX, 0, 0 } +}; + +// This table must be in the same order as the ..._FAMILY_INDEX enum specifies +static const FontFamilyRec gFamilies[] = { + { gDefaultFaces, SK_ARRAY_COUNT(gDefaultFaces) } +}; + +#define DEFAULT_FAMILY_INDEX DEFAULT_FAMILY_INDEX +#define DEFAULT_FAMILY_FACE_INDEX 0 + +/////////////////////////////////////////////////////////////////////////////// + +/* map common "web" font names to our font list */ + +struct FontFamilyMatchRec { + const char* fLCName; + int fFamilyIndex; +}; + +/* This is a table of synonyms for collapsing font names + down to their pseudo-equivalents (i.e. in terms of fonts + we actually have.) + Keep this sorted by the first field so we can do a binary search. + If this gets big, we could switch to a hash... +*/ +static const FontFamilyMatchRec gMatches[] = { +#if 0 + { "Ahem", Ahem_FAMILY_INDEX }, + { "arial", SANS_FAMILY_INDEX }, + { "courier", MONO_FAMILY_INDEX }, + { "courier new", MONO_FAMILY_INDEX }, + { "cursive", SERIF_FAMILY_INDEX }, + { "fantasy", SERIF_FAMILY_INDEX }, + { "georgia", SERIF_FAMILY_INDEX }, + { "goudy", SERIF_FAMILY_INDEX }, + { "helvetica", SANS_FAMILY_INDEX }, + { "palatino", SERIF_FAMILY_INDEX }, + { "tahoma", SANS_FAMILY_INDEX }, + { "sans-serif", SANS_FAMILY_INDEX }, + { "serif", SERIF_FAMILY_INDEX }, + { "times", SERIF_FAMILY_INDEX }, + { "times new roman", SERIF_FAMILY_INDEX }, + { "verdana", SANS_FAMILY_INDEX } +#endif +}; + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkTSearch.h" + +static bool contains_only_ascii(const char s[]) +{ + for (;;) + { + int c = *s++; + if (c == 0) + break; + if ((c >> 7) != 0) + return false; + } + return true; +} + +#define TRACE_FONT_NAME(code) +//#define TRACE_FONT_NAME(code) code + +const FontFamilyRec* find_family_rec(const char target[]) +{ + int index; + + // If we're asked for a font name that contains non-ascii, + // 1) SkStrLCSearch can't handle it + // 2) All of our fonts are have ascii names, so... + +TRACE_FONT_NAME(printf("----------------- font request <%s>", target);) + + if (contains_only_ascii(target)) + { + // Search for the font by matching the entire name + index = SkStrLCSearch(&gMatches[0].fLCName, SK_ARRAY_COUNT(gMatches), + target, sizeof(gMatches[0])); + if (index >= 0) + { + TRACE_FONT_NAME(printf(" found %d\n", index);) + return &gFamilies[gMatches[index].fFamilyIndex]; + } + } + + // Sniff for key words... + +#if 0 + if (strstr(target, "sans") || strstr(target, "Sans")) + { + TRACE_FONT_NAME(printf(" found sans\n");) + return &gFamilies[SANS_FAMILY_INDEX]; + } + if (strstr(target, "serif") || strstr(target, "Serif")) + { + TRACE_FONT_NAME(printf(" found serif\n");) + return &gFamilies[SERIF_FAMILY_INDEX]; + } + if (strstr(target, "mono") || strstr(target, "Mono")) + { + TRACE_FONT_NAME(printf(" found mono\n");) + return &gFamilies[MONO_FAMILY_INDEX]; + } +#endif + + TRACE_FONT_NAME(printf(" use default\n");) + // we give up, just give them the default font + return &gFamilies[DEFAULT_FAMILY_INDEX]; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const FontFaceRec* get_default_face() +{ + return &gFamilies[DEFAULT_FAMILY_INDEX].fFaces[DEFAULT_FAMILY_FACE_INDEX]; +} + +static SkTypeface::Style get_style(const FontFaceRec& face) { + int style = 0; + if (face.fBold) { + style |= SkTypeface::kBold; + } + if (face.fItalic) { + style |= SkTypeface::kItalic; + } + return static_cast<SkTypeface::Style>(style); +} + +// This global const reference completely identifies the face +static uint32_t get_id(const FontFaceRec& face) { + uintptr_t id = reinterpret_cast<uintptr_t>(&face); + return static_cast<uint32_t>(id); +} + +class FontFaceRec_Typeface : public SkTypeface { +public: + FontFaceRec_Typeface(const FontFaceRec& face) : + SkTypeface(get_style(face), get_id(face)), + fFace(face) + { + } + + // This global const reference completely identifies the face + const FontFaceRec& fFace; +}; + +static const FontFaceRec* get_typeface_rec(const SkTypeface* face) +{ + const FontFaceRec_Typeface* f = (FontFaceRec_Typeface*)face; + return f ? &f->fFace : get_default_face(); +} + +static uint32_t ptr2uint32(const void* p) +{ + // cast so we avoid warnings on 64bit machines that a ptr difference + // which might be 64bits is being trucated from 64 to 32 + return (uint32_t)((char*)p - (char*)0); +} + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char familyName[], + const void* data, size_t bytelength, + SkTypeface::Style style) +{ + const FontFamilyRec* family; + + if (familyFace) + family = &gFamilies[ + ((FontFaceRec_Typeface*)familyFace)->fFace.fFamilyIndex]; + else if (familyName) + family = find_family_rec(familyName); + else + family = &gFamilies[DEFAULT_FAMILY_INDEX]; + + const FontFaceRec& face = FontFaceRec::FindFace(family->fFaces, + family->fFaceCount, + (style & SkTypeface::kBold) != 0, + (style & SkTypeface::kItalic) != 0); + + // if we're returning our input parameter, no need to create a new instance + if (familyFace != NULL && + &((FontFaceRec_Typeface*)familyFace)->fFace == &face) + { + familyFace->ref(); + return (SkTypeface*)familyFace; + } + return SkNEW_ARGS(FontFaceRec_Typeface, (face)); +} + +// static +SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( + uint32_t fontID, bool perGlyphInfo) { + sk_throw(); // not implemented + return NULL; +} + +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { + sk_throw(); // not implemented + return NULL; +} + +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { + sk_throw(); // not implemented + return NULL; +} + +bool SkFontHost::ValidFontID(uint32_t fontID) { + return get_id(*get_default_face()) == fontID; +} + +SkStream* SkFontHost::OpenStream(uint32_t fontID) { + sk_throw(); // not implemented + return NULL; +} + +size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, + int32_t* index) { + SkDebugf("SkFontHost::GetFileName unimplemented\n"); + return 0; +} + +void SkFontHost::Serialize(const SkTypeface* tface, SkWStream* stream) { + const FontFaceRec* face = &((const FontFaceRec_Typeface*)tface)->fFace; + stream->write(face, sizeof(face)); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + const FontFaceRec* face; + stream->read(&face, sizeof(face)); + return new FontFaceRec_Typeface(*face); +} + +SkScalerContext* SkFontHost::CreateFallbackScalerContext( + const SkScalerContext::Rec& rec) +{ + const FontFaceRec* face = get_default_face(); + + SkAutoDescriptor ad(sizeof(rec) + sizeof(face) + + SkDescriptor::ComputeOverhead(2)); + SkDescriptor* desc = ad.getDesc(); + SkScalerContext::Rec* newRec; + + desc->init(); + newRec = reinterpret_cast<SkScalerContext::Rec*>( + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec)); + newRec->fFontID = get_id(*face); + desc->computeChecksum(); + + return SkFontHost::CreateScalerContext(desc); +} + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + return 0; // nothing to do (change me if you want to limit the font cache) +} + diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index a03e63d..a216708 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -15,24 +15,30 @@ ** limitations under the License. */ -#include "SkColorPriv.h" -#include "SkScalerContext.h" #include "SkBitmap.h" #include "SkCanvas.h" +#include "SkColorPriv.h" #include "SkDescriptor.h" #include "SkFDot6.h" #include "SkFontHost.h" #include "SkMask.h" +#include "SkAdvancedTypefaceMetrics.h" +#include "SkScalerContext.h" #include "SkStream.h" #include "SkString.h" -#include "SkThread.h" #include "SkTemplates.h" +#include "SkThread.h" #include <ft2build.h> #include FT_FREETYPE_H #include FT_OUTLINE_H #include FT_SIZES_H #include FT_TRUETYPE_TABLES_H +#include FT_TYPE1_TABLES_H +#include FT_BITMAP_H +// In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file. +#include FT_SYNTHESIS_H +#include FT_XFREE86_H #if defined(SK_SUPPORT_LCDTEXT) #include FT_LCD_FILTER_H @@ -50,6 +56,8 @@ #include <freetype/tttables.h> #include <freetype/ftadvanc.h> #include <freetype/ftlcdfil.h> +#include <freetype/ftbitmap.h> +#include <freetype/ftsynth.h> #endif //#define ENABLE_GLYPH_SPEW // for tracing calls @@ -65,6 +73,8 @@ #define SkASSERT_CONTINUE(pred) #endif +using namespace skia_advanced_typeface_metrics_utils; + ////////////////////////////////////////////////////////////////////////// struct SkFaceRec; @@ -78,6 +88,10 @@ static bool gLCDSupport; // true iff LCD is supported by the runtime. ///////////////////////////////////////////////////////////////////////// +// See http://freetype.sourceforge.net/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden +// This value was chosen by eyeballing the result in Firefox and trying to match it. +static const FT_Pos kBitmapEmboldenStrength = 1 << 6; + static bool InitFreetype() { FT_Error err = FT_Init_FreeType(&gFTLibrary); @@ -227,14 +241,10 @@ static SkFaceRec* ref_ft_face(uint32_t fontID) { args.stream = &rec->fFTStream; } - if (gFTCount == 0) { - if (!InitFreetype()) { - return 0; - } - } - ++gFTCount; - - FT_Error err = FT_Open_Face(gFTLibrary, &args, 0, &rec->fFace); + int face_index; + int length = SkFontHost::GetFileName(fontID, NULL, 0, &face_index); + FT_Error err = FT_Open_Face(gFTLibrary, &args, length ? face_index : 0, + &rec->fFace); if (err) { // bad filename, try the default font fprintf(stderr, "ERROR: unable to open font '%x'\n", fontID); @@ -264,10 +274,6 @@ static void unref_ft_face(FT_Face face) { } FT_Done_Face(face); SkDELETE(rec); - - if (--gFTCount == 0) { - FT_Done_FreeType(gFTLibrary); - } } return; } @@ -279,23 +285,256 @@ static void unref_ft_face(FT_Face face) { /////////////////////////////////////////////////////////////////////////// +// Work around for old versions of freetype. +static FT_Error getAdvances(FT_Face face, FT_UInt start, FT_UInt count, + FT_Int32 loadFlags, FT_Fixed* advances) { +#ifdef FT_ADVANCES_H + return FT_Get_Advances(face, start, count, loadFlags, advances); +#else + if (!face || start >= face->num_glyphs || + start + count > face->num_glyphs || loadFlags != FT_LOAD_NO_SCALE) { + return 6; // "Invalid argument." + } + if (count == 0) + return 0; + + for (int i = 0; i < count; i++) { + FT_Error err = FT_Load_Glyph(face, start + i, FT_LOAD_NO_SCALE); + if (err) + return err; + advances[i] = face->glyph->advance.x; + } + + return 0; +#endif +} + +static bool canEmbed(FT_Face face) { +#ifdef FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING + FT_UShort fsType = FT_Get_FSType_Flags(face); + return (fsType & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING | + FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) == 0; +#else + // No embedding is 0x2 and bitmap embedding only is 0x200. + TT_OS2* os2_table; + if ((os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) { + return (os2_table->fsType & 0x202) == 0; + } + return false; // We tried, fail safe. +#endif +} + +static bool GetLetterCBox(FT_Face face, char letter, FT_BBox* bbox) { + const FT_UInt glyph_id = FT_Get_Char_Index(face, letter); + if (!glyph_id) + return false; + FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_SCALE); + FT_Outline_Get_CBox(&face->glyph->outline, bbox); + return true; +} + +static bool getWidthAdvance(FT_Face face, int gId, int16_t* data) { + FT_Fixed advance = 0; + if (getAdvances(face, gId, 1, FT_LOAD_NO_SCALE, &advance)) { + return false; + } + SkASSERT(data); + *data = advance; + return true; +} + +// static +SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( + uint32_t fontID, bool perGlyphInfo) { +#ifdef ANDROID + return NULL; +#else + SkAutoMutexAcquire ac(gFTMutex); + FT_Library libInit = NULL; + if (gFTCount == 0) { + if (!InitFreetype()) + sk_throw(); + libInit = gFTLibrary; + } + SkAutoTCallIProc<struct FT_LibraryRec_, FT_Done_FreeType> ftLib(libInit); + SkFaceRec* rec = ref_ft_face(fontID); + if (NULL == rec) + return NULL; + FT_Face face = rec->fFace; + + SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics; + info->fFontName.set(FT_Get_Postscript_Name(face)); + info->fMultiMaster = FT_HAS_MULTIPLE_MASTERS(face); + info->fLastGlyphID = face->num_glyphs - 1; + info->fEmSize = 1000; + + bool cid = false; + const char* fontType = FT_Get_X11_Font_Format(face); + if (strcmp(fontType, "Type 1") == 0) { + info->fType = SkAdvancedTypefaceMetrics::kType1_Font; + } else if (strcmp(fontType, "CID Type 1") == 0) { + info->fType = SkAdvancedTypefaceMetrics::kType1CID_Font; + cid = true; + } else if (strcmp(fontType, "CFF") == 0) { + info->fType = SkAdvancedTypefaceMetrics::kCFF_Font; + } else if (strcmp(fontType, "TrueType") == 0) { + info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; + cid = true; + TT_Header* ttHeader; + if ((ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face, + ft_sfnt_head)) != NULL) { + info->fEmSize = ttHeader->Units_Per_EM; + } + } + + SkASSERT(!FT_HAS_VERTICAL(face)); +#ifdef FT_IS_CID_KEYED + SkASSERT(FT_IS_CID_KEYED(face) == + (info->fType == SkAdvancedTypefaceMetrics::kType1CID_Font)); +#endif + + info->fStyle = 0; + if (FT_IS_FIXED_WIDTH(face)) + info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; + if (face->style_flags & FT_STYLE_FLAG_ITALIC) + info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; + // We should set either Symbolic or Nonsymbolic; Nonsymbolic if the font's + // character set is a subset of 'Adobe standard Latin.' + info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style; + + PS_FontInfoRec ps_info; + TT_Postscript* tt_info; + if (FT_Get_PS_Font_Info(face, &ps_info) == 0) { + info->fItalicAngle = ps_info.italic_angle; + } else if ((tt_info = + (TT_Postscript*)FT_Get_Sfnt_Table(face, + ft_sfnt_post)) != NULL) { + info->fItalicAngle = SkFixedToScalar(tt_info->italicAngle); + } else { + info->fItalicAngle = 0; + } + + info->fAscent = face->ascender; + info->fDescent = face->descender; + + // 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; + char stem_chars[] = {'i', 'I', '!', '1'}; + for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) { + FT_BBox bbox; + if (GetLetterCBox(face, stem_chars[i], &bbox)) { + int16_t width = bbox.xMax - bbox.xMin; + if (width > 0 && width < min_width) { + min_width = width; + info->fStemV = min_width; + } + } + } + + TT_PCLT* pclt_info; + TT_OS2* os2_table; + if ((pclt_info = (TT_PCLT*)FT_Get_Sfnt_Table(face, ft_sfnt_pclt)) != NULL) { + info->fCapHeight = pclt_info->CapHeight; + uint8_t serif_style = pclt_info->SerifStyle & 0x3F; + if (serif_style >= 2 && serif_style <= 6) + info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; + else if (serif_style >= 9 && serif_style <= 12) + info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; + } else if ((os2_table = + (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) { + info->fCapHeight = os2_table->sCapHeight; + } else { + // Figure out a good guess for CapHeight: average the height of M and X. + FT_BBox m_bbox, x_bbox; + bool got_m, got_x; + got_m = GetLetterCBox(face, 'M', &m_bbox); + got_x = GetLetterCBox(face, 'X', &x_bbox); + if (got_m && got_x) { + info->fCapHeight = (m_bbox.yMax - m_bbox.yMin + x_bbox.yMax - + x_bbox.yMin) / 2; + } else if (got_m && !got_x) { + info->fCapHeight = m_bbox.yMax - m_bbox.yMin; + } else if (!got_m && got_x) { + info->fCapHeight = x_bbox.yMax - x_bbox.yMin; + } + } + + info->fBBox = SkIRect::MakeLTRB(face->bbox.xMin, face->bbox.yMax, + face->bbox.xMax, face->bbox.yMin); + + if (perGlyphInfo && canEmbed(face) && FT_IS_SCALABLE(face) && + info->fType != SkAdvancedTypefaceMetrics::kOther_Font) { + if (FT_IS_FIXED_WIDTH(face)) { + appendRange(&info->fGlyphWidths, 0); + int16_t advance = face->max_advance_width; + info->fGlyphWidths->fAdvance.append(1, &advance); + finishRange(info->fGlyphWidths.get(), 0, + SkAdvancedTypefaceMetrics::WidthRange::kDefault); + } else if (!cid) { + appendRange(&info->fGlyphWidths, 0); + // So as to not blow out the stack, get advances in batches. + for (int gID = 0; gID < face->num_glyphs; gID += 128) { + FT_Fixed advances[128]; + int advanceCount = 128; + if (gID + advanceCount > face->num_glyphs) + advanceCount = face->num_glyphs - gID + 1; + getAdvances(face, gID, advanceCount, FT_LOAD_NO_SCALE, + advances); + for (int i = 0; i < advanceCount; i++) { + int16_t advance = advances[gID + i]; + info->fGlyphWidths->fAdvance.append(1, &advance); + } + } + finishRange(info->fGlyphWidths.get(), face->num_glyphs - 1, + SkAdvancedTypefaceMetrics::WidthRange::kRange); + } else { + info->fGlyphWidths.reset( + getAdvanceData(face, face->num_glyphs, &getWidthAdvance)); + } + + if (info->fType == SkAdvancedTypefaceMetrics::kType1_Font) { + // Postscript fonts may contain more than 255 glyphs, so we end up + // using multiple font descriptions with a glyph ordering. Record + // the name of each glyph. + info->fGlyphNames.reset( + new SkAutoTArray<SkString>(face->num_glyphs)); + for (int gID = 0; gID < face->num_glyphs; gID++) { + char glyphName[128]; // PS limit for names is 127 bytes. + FT_Get_Glyph_Name(face, gID, glyphName, 128); + info->fGlyphNames->get()[gID].set(glyphName); + } + } + } + + if (!canEmbed(face)) + info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font; + + unref_ft_face(face); + return info; +#endif +} +/////////////////////////////////////////////////////////////////////////// + void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { if (!gLCDSupportValid) { - InitFreetype(); - FT_Done_FreeType(gFTLibrary); + InitFreetype(); + FT_Done_FreeType(gFTLibrary); } - + if (!gLCDSupport && rec->isLCD()) { - // If the runtime Freetype library doesn't support LCD mode, we disable - // it here. - rec->fMaskFormat = SkMask::kA8_Format; + // If the runtime Freetype library doesn't support LCD mode, we disable + // it here. + rec->fMaskFormat = SkMask::kA8_Format; } SkPaint::Hinting h = rec->getHinting(); if (SkPaint::kFull_Hinting == h && !rec->isLCD()) { // collapse full->normal hinting if we're not doing LCD h = SkPaint::kNormal_Hinting; - } else if (rec->fSubpixelPositioning && SkPaint::kNo_Hinting != h) { + } else if ((rec->fFlags & SkScalerContext::kSubpixelPositioning_Flag) && + SkPaint::kNo_Hinting != h) { // to do subpixel, we must have at most slight hinting h = SkPaint::kSlight_Hinting; } @@ -380,26 +619,40 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) { FT_Int32 loadFlags = FT_LOAD_DEFAULT; - switch (fRec.getHinting()) { - case SkPaint::kNo_Hinting: - loadFlags = FT_LOAD_NO_HINTING; - break; - case SkPaint::kSlight_Hinting: - loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT - break; - case SkPaint::kNormal_Hinting: - loadFlags = FT_LOAD_TARGET_NORMAL; - break; - case SkPaint::kFull_Hinting: - loadFlags = FT_LOAD_TARGET_NORMAL; - if (SkMask::kHorizontalLCD_Format == fRec.fMaskFormat) - loadFlags = FT_LOAD_TARGET_LCD; - else if (SkMask::kVerticalLCD_Format == fRec.fMaskFormat) - loadFlags = FT_LOAD_TARGET_LCD_V; - break; - default: - SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting()); - break; + if (SkMask::kBW_Format == fRec.fMaskFormat) { + // See http://code.google.com/p/chromium/issues/detail?id=43252#c24 + loadFlags = FT_LOAD_TARGET_MONO; + if (fRec.getHinting() == SkPaint::kNo_Hinting) + loadFlags = FT_LOAD_NO_HINTING; + } else { + switch (fRec.getHinting()) { + case SkPaint::kNo_Hinting: + loadFlags = FT_LOAD_NO_HINTING; + break; + case SkPaint::kSlight_Hinting: + loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT + break; + case SkPaint::kNormal_Hinting: + if (fRec.fFlags & SkScalerContext::kAutohinting_Flag) + loadFlags = FT_LOAD_FORCE_AUTOHINT; + else + loadFlags = FT_LOAD_NO_AUTOHINT; + break; + case SkPaint::kFull_Hinting: + if (fRec.fFlags & SkScalerContext::kAutohinting_Flag) { + loadFlags = FT_LOAD_FORCE_AUTOHINT; + break; + } + loadFlags = FT_LOAD_TARGET_NORMAL; + if (SkMask::kHorizontalLCD_Format == fRec.fMaskFormat) + loadFlags = FT_LOAD_TARGET_LCD; + else if (SkMask::kVerticalLCD_Format == fRec.fMaskFormat) + loadFlags = FT_LOAD_TARGET_LCD_V; + break; + default: + SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting()); + break; + } } if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) @@ -595,7 +848,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); - if (fRec.fSubpixelPositioning) { + if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { int dx = glyph->getSubXFixed() >> 10; int dy = glyph->getSubYFixed() >> 10; // negate dy since freetype-y-goes-up and skia-y-goes-down @@ -617,6 +870,10 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { break; case FT_GLYPH_FORMAT_BITMAP: + if (fRec.fFlags & kEmbolden_Flag) { + FT_GlyphSlot_Own_Bitmap(fFace->glyph); + FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0); + } glyph->fWidth = SkToU16(fFace->glyph->bitmap.width); glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows); glyph->fTop = -SkToS16(fFace->glyph->bitmap_top); @@ -628,7 +885,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { goto ERROR; } - if (!fRec.fSubpixelPositioning) { + if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) { glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x); glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y); if (fRec.fFlags & kDevKernText_Flag) { @@ -688,7 +945,7 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { } int dx = 0, dy = 0; - if (fRec.fSubpixelPositioning) { + if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { dx = glyph.getSubXFixed() >> 10; dy = glyph.getSubYFixed() >> 10; // negate dy since freetype-y-goes-up and skia-y-goes-down @@ -736,6 +993,10 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { } break; case FT_GLYPH_FORMAT_BITMAP: { + if (fRec.fFlags & kEmbolden_Flag) { + FT_GlyphSlot_Own_Bitmap(fFace->glyph); + FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0); + } SkASSERT_CONTINUE(glyph.fWidth == fFace->glyph->bitmap.width); SkASSERT_CONTINUE(glyph.fHeight == fFace->glyph->bitmap.rows); SkASSERT_CONTINUE(glyph.fTop == -fFace->glyph->bitmap_top); diff --git a/src/ports/SkFontHost_FreeType_Subpixel.cpp b/src/ports/SkFontHost_FreeType_Subpixel.cpp new file mode 100644 index 0000000..bc01585 --- /dev/null +++ b/src/ports/SkFontHost_FreeType_Subpixel.cpp @@ -0,0 +1,143 @@ +/* libs/graphics/ports/SkFontHost_FreeType_Subpixel.cpp +** +** Copyright 2009, 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 file contains functions for converting Freetype's subpixel output + formats into the format used by SkMask for subpixel masks. See the comments + in SkMask.h for details on the format. +*/ + +#include "SkColorPriv.h" +#include "SkFontHost.h" +#include "SkMask.h" +#include "SkScalerContext.h" + +#include <ft2build.h> +#include FT_FREETYPE_H + +#if 0 +// Also include the files by name for build tools which require this. +#include <freetype/freetype.h> +#endif + +namespace skia_freetype_support { + +void CopyFreetypeBitmapToLCDMask(const SkGlyph& dest, const FT_Bitmap& source) +{ + // |source| has three alpha values per pixel and has an extra column at the + // left and right edges. + + // ----- <--- a single pixel in the output + // source .oOo.. |.oO|o.. + // .OOO.. ----- + // .oOo.. --> .OO O.. + // .oO o.. + + uint8_t* output = reinterpret_cast<uint8_t*>(dest.fImage); + const unsigned outputPitch = SkAlign4((source.width / 3) - 2); + const uint8_t* input = source.buffer; + + // First we calculate the A8 mask. + for (int y = 0; y < source.rows; ++y) { + const uint8_t* inputRow = input; + uint8_t* outputRow = output; + inputRow += 3; // skip the extra column on the left + for (int x = 3; x < source.width - 3; x += 3) { + const uint8_t averageAlpha = (static_cast<unsigned>(inputRow[0]) + inputRow[1] + inputRow[2] + 1) / 3; + *outputRow++ = averageAlpha; + inputRow += 3; + } + + input += source.pitch; + output += outputPitch; + } + + // Align the 32-bit plane on a word boundary + uint32_t* output32 = (uint32_t*) SkAlign4((uintptr_t) output); + + // Now we build the 32-bit alpha mask and RGB order correct. + const int isBGR = SkFontHost::GetSubpixelOrder() == SkFontHost::kBGR_LCDOrder; + input = source.buffer; + + for (int y = 0; y < source.rows; ++y) { + const uint8_t* inputRow = input; + for (int x = 0; x < source.width; x += 3) { + const uint8_t alphaRed = isBGR ? inputRow[2] : inputRow[0]; + const uint8_t alphaGreen = inputRow[1]; + const uint8_t alphaBlue = isBGR ? inputRow[0] : inputRow[2]; + const uint8_t maxAlpha = SkMax32(alphaRed, SkMax32(alphaGreen, alphaBlue)); + *output32++ = SkPackARGB32(maxAlpha, alphaRed, alphaGreen, alphaBlue); + + inputRow += 3; + } + + input += source.pitch; + } +} + +void CopyFreetypeBitmapToVerticalLCDMask(const SkGlyph& dest, const FT_Bitmap& source) +{ + // |source| has three times as many rows as normal, and an extra triple on the + // top and bottom. + + // source .oOo.. |.|oOo.. + // .OOO.. --> |.|OOO.. + // .oOo.. |.|oOo.. + // ^ + // |-------- A single pixel in the output + + uint8_t* output = reinterpret_cast<uint8_t*>(dest.fImage); + const unsigned outputPitch = dest.rowBytes(); + const uint8_t* input = source.buffer; + + // First we calculate the A8 mask. + input += 3 * source.pitch; // skip the extra at the beginning + for (int y = 3; y < source.rows - 3; y += 3) { + const uint8_t* inputRow = input; + uint8_t* outputRow = output; + for (int x = 0; x < source.width; ++x) { + const uint8_t averageAlpha = (static_cast<unsigned>(*inputRow) + inputRow[source.pitch] + inputRow[source.pitch * 2] + 1) / 3; + *outputRow++ = averageAlpha; + inputRow++; + } + + input += source.pitch * 3; + output += outputPitch; + } + + // Align the 32-bit plane on a word boundary + uint32_t* output32 = (uint32_t*) SkAlign4((uintptr_t) output); + + // Now we build the 32-bit alpha mask and RGB order correct. + const int isBGR = SkFontHost::GetSubpixelOrder() == SkFontHost::kBGR_LCDOrder; + input = source.buffer; + + for (int y = 0; y < source.rows; y += 3) { + const uint8_t* inputRow = input; + for (int x = 0; x < source.width; ++x) { + const uint8_t alphaRed = isBGR ? inputRow[source.pitch * 2] : inputRow[0]; + const uint8_t alphaGreen = inputRow[source.pitch]; + const uint8_t alphaBlue = isBGR ? inputRow[0] : inputRow[2 * source.pitch]; + const uint8_t maxAlpha = SkMax32(alphaRed, SkMax32(alphaGreen, alphaBlue)); + *output32++ = SkPackARGB32(maxAlpha, alphaRed, alphaGreen, alphaBlue); + inputRow++; + } + + input += source.pitch * 3; + } +} + +} // namespace skia_freetype_support diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index a5136d6..7e515d0 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -51,7 +51,7 @@ struct FamilyRec; struct NameFamilyPair { const char* fName; // we own this FamilyRec* fFamily; // we don't own this, we just reference it - + void construct(const char name[], FamilyRec* family) { fName = strdup(name); fFamily = family; // we don't own this, so just record the referene @@ -74,7 +74,7 @@ static SkTDArray<NameFamilyPair> gNameList; struct FamilyRec { FamilyRec* fNext; SkTypeface* fFaces[4]; - + FamilyRec() { fNext = gFamilyHead; @@ -147,7 +147,7 @@ static FamilyRec* remove_from_family(const SkTypeface* face) { FamilyRec* family = find_family(face); SkASSERT(family->fFaces[face->style()] == face); family->fFaces[face->style()] = NULL; - + for (int i = 0; i < 4; i++) { if (family->fFaces[i] != NULL) { // family is non-empty return NULL; @@ -181,7 +181,7 @@ static void detach_and_delete_family(FamilyRec* family) { static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) { NameFamilyPair* list = gNameList.begin(); int count = gNameList.count(); - + int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); if (index >= 0) { @@ -202,7 +202,7 @@ static void add_name(const char name[], FamilyRec* family) { NameFamilyPair* list = gNameList.begin(); int count = gNameList.count(); - + int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); if (index < 0) { @@ -220,7 +220,7 @@ static void remove_from_names(FamilyRec* emptyFamily) #endif SkTDArray<NameFamilyPair>& list = gNameList; - + // must go backwards when removing for (int i = list.count() - 1; i >= 0; --i) { NameFamilyPair* pair = &list[i]; @@ -238,9 +238,9 @@ public: FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember) : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) { fIsSysFont = sysFont; - + SkAutoMutexAcquire ac(gFamilyMutex); - + FamilyRec* rec = NULL; if (familyMember) { rec = find_family(familyMember); @@ -250,10 +250,10 @@ public: } rec->fFaces[style] = this; } - + virtual ~FamilyTypeface() { SkAutoMutexAcquire ac(gFamilyMutex); - + // remove us from our family. If the family is now empty, we return // that and then remove that family from the name list FamilyRec* family = remove_from_family(this); @@ -262,16 +262,16 @@ public: detach_and_delete_family(family); } } - + bool isSysFont() const { return fIsSysFont; } - + virtual SkStream* openStream() = 0; virtual const char* getUniqueString() const = 0; virtual const char* getFilePath() const = 0; - + private: bool fIsSysFont; - + typedef SkTypeface INHERITED; }; @@ -289,12 +289,14 @@ public: virtual ~StreamTypeface() { fStream->unref(); } - + // overrides virtual SkStream* openStream() { // we just ref our existing stream, since the caller will call unref() // when they are through fStream->ref(); + // must rewind each time, since the caller assumes a "new" stream + fStream->rewind(); return fStream; } virtual const char* getUniqueString() const { return NULL; } @@ -302,7 +304,7 @@ public: private: SkStream* fStream; - + typedef FamilyTypeface INHERITED; }; @@ -312,14 +314,14 @@ public: const char path[]) : INHERITED(style, sysFont, familyMember) { SkString fullpath; - + if (sysFont) { GetFullPathForSysFonts(&fullpath, path); path = fullpath.c_str(); } fPath.set(path); } - + // overrides virtual SkStream* openStream() { SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str())); @@ -349,7 +351,7 @@ public: private: SkString fPath; - + typedef FamilyTypeface INHERITED; }; @@ -448,7 +450,7 @@ static void load_system_fonts() { if (NULL != gDefaultNormal) { return; } - + const FontInitRec* rec = gSystemFonts; SkTypeface* firstInFamily = NULL; int fallbackCount = 0; @@ -458,7 +460,7 @@ static void load_system_fonts() { if (rec[i].fNames != NULL) { firstInFamily = NULL; } - + SkString name; SkTypeface::Style style; @@ -534,7 +536,7 @@ SkTypeface* SkFontHost::Deserialize(SkStream* stream) { SkString str; str.resize(len); stream->read(str.writable_str(), len); - + const FontInitRec* rec = gSystemFonts; for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { if (strcmp(rec[i].fFileName, str.c_str()) == 0) { @@ -542,7 +544,7 @@ SkTypeface* SkFontHost::Deserialize(SkStream* stream) { for (int j = i; j >= 0; --j) { if (rec[j].fNames != NULL) { return SkFontHost::CreateTypeface(NULL, - rec[j].fNames[0], (SkTypeface::Style)style); + rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style); } } } @@ -555,14 +557,15 @@ SkTypeface* SkFontHost::Deserialize(SkStream* stream) { SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, const char familyName[], + const void* data, size_t bytelength, SkTypeface::Style style) { load_system_fonts(); SkAutoMutexAcquire ac(gFamilyMutex); - + // clip to legal style bits style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic); - + SkTypeface* tf = NULL; if (NULL != familyFace) { @@ -583,13 +586,13 @@ SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, bool SkFontHost::ValidFontID(uint32_t fontID) { SkAutoMutexAcquire ac(gFamilyMutex); - + return find_from_uniqueID(fontID) != NULL; } SkStream* SkFontHost::OpenStream(uint32_t fontID) { SkAutoMutexAcquire ac(gFamilyMutex); - + FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID); SkStream* stream = tf ? tf->openStream() : NULL; @@ -644,7 +647,7 @@ SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { if (NULL == stream || stream->getLength() <= 0) { return NULL; } - + SkString name; SkTypeface::Style style; diff --git a/src/ports/SkFontHost_ascender.cpp b/src/ports/SkFontHost_ascender.cpp new file mode 100644 index 0000000..88cde38 --- /dev/null +++ b/src/ports/SkFontHost_ascender.cpp @@ -0,0 +1,223 @@ +#include "SkScalerContext.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkDescriptor.h" +#include "SkFDot6.h" +#include "SkFontHost.h" +#include "SkMask.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkThread.h" +#include "SkTemplates.h" + +#include <acaapi.h> + +////////////////////////////////////////////////////////////////////////// + +#include "SkMMapStream.h" + +class SkScalerContext_Ascender : public SkScalerContext { +public: + SkScalerContext_Ascender(const SkDescriptor* desc); + virtual ~SkScalerContext_Ascender(); + +protected: + virtual unsigned generateGlyphCount() const; + virtual uint16_t generateCharToGlyph(SkUnichar uni); + virtual void generateMetrics(SkGlyph* glyph); + virtual void generateImage(const SkGlyph& glyph); + virtual void generatePath(const SkGlyph& glyph, SkPath* path); + virtual void generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my); + +private: + aca_FontHandle fHandle; + void* fWorkspace; + void* fGlyphWorkspace; + SkStream* fFontStream; + SkStream* fHintStream; +}; + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +SkScalerContext_Ascender::SkScalerContext_Ascender(const SkDescriptor* desc) + : SkScalerContext(desc) +{ + int size = aca_Get_FontHandleRec_Size(); + fHandle = (aca_FontHandle)sk_malloc_throw(size); + + // get the pointer to the font + + fFontStream = new SkMMAPStream("/UcsGB2312-Hei-H.FDL"); + fHintStream = new SkMMAPStream("/genv6-23.bin"); + + void* hints = sk_malloc_throw(fHintStream->getLength()); + memcpy(hints, fHintStream->getMemoryBase(), fHintStream->getLength()); + + aca_Create_Font_Handle(fHandle, + (void*)fFontStream->getMemoryBase(), fFontStream->getLength(), + "fred", + hints, fHintStream->getLength()); + + // compute our factors from the record + + SkMatrix m; + + fRec.getSingleMatrix(&m); + + // now compute our scale factors + SkScalar sx = m.getScaleX(); + SkScalar sy = m.getScaleY(); + + int ppemX = SkScalarRound(sx); + int ppemY = SkScalarRound(sy); + + size = aca_Find_Font_Memory_Required(fHandle, ppemX, ppemY); + size *= 8; // Jeff suggests this :) + fWorkspace = sk_malloc_throw(size); + aca_Set_Font_Memory(fHandle, (uint8_t*)fWorkspace, size); + + aca_GlyphAttribsRec rec; + + memset(&rec, 0, sizeof(rec)); + rec.xSize = ppemX; + rec.ySize = ppemY; + rec.doAdjust = true; + rec.doExceptions = true; + rec.doGlyphHints = true; + rec.doInterpolate = true; + rec.grayMode = 2; + aca_Set_Font_Attributes(fHandle, &rec, &size); + + fGlyphWorkspace = sk_malloc_throw(size); + aca_Set_Glyph_Memory(fHandle, fGlyphWorkspace); +} + +SkScalerContext_Ascender::~SkScalerContext_Ascender() +{ + delete fHintStream; + delete fFontStream; + sk_free(fGlyphWorkspace); + sk_free(fWorkspace); + sk_free(fHandle); +} + +unsigned SkScalerContext_Ascender::generateGlyphCount() const +{ + return 1000; +} + +uint16_t SkScalerContext_Ascender::generateCharToGlyph(SkUnichar uni) +{ + return (uint16_t)(uni & 0xFFFF); +} + +void SkScalerContext_Ascender::generateMetrics(SkGlyph* glyph) +{ + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + + aca_GlyphImageRec rec; + aca_Vector topLeft; + + int adv = aca_Get_Adv_Width(fHandle, glyph->getGlyphID()); + if (aca_GLYPH_NOT_PRESENT == adv) + goto ERROR; + + aca_Rasterize(glyph->getGlyphID(), fHandle, &rec, &topLeft); + + if (false) // error + { +ERROR: + glyph->fWidth = 0; + glyph->fHeight = 0; + glyph->fTop = 0; + glyph->fLeft = 0; + glyph->fAdvanceX = 0; + glyph->fAdvanceY = 0; + return; + } + + glyph->fWidth = rec.width; + glyph->fHeight = rec.rows; + glyph->fRowBytes = rec.width; + glyph->fTop = -topLeft.y; + glyph->fLeft = topLeft.x; + glyph->fAdvanceX = SkIntToFixed(adv); + glyph->fAdvanceY = SkIntToFixed(0); +} + +void SkScalerContext_Ascender::generateImage(const SkGlyph& glyph) +{ + aca_GlyphImageRec rec; + aca_Vector topLeft; + + aca_Rasterize(glyph.getGlyphID(), fHandle, &rec, &topLeft); + + const uint8_t* src = (const uint8_t*)rec.buffer; + uint8_t* dst = (uint8_t*)glyph.fImage; + int height = glyph.fHeight; + + src += rec.y0 * rec.pitch + rec.x0; + while (--height >= 0) + { + memcpy(dst, src, glyph.fWidth); + src += rec.pitch; + dst += glyph.fRowBytes; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +void SkScalerContext_Ascender::generatePath(const SkGlyph& glyph, SkPath* path) +{ + SkRect r; + + r.set(0, 0, SkIntToScalar(4), SkIntToScalar(4)); + path->reset(); + path->addRect(r); +} + +void SkScalerContext_Ascender::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) +{ + if (NULL == mx && NULL == my) + return; + + if (mx) + { + mx->fTop = SkIntToScalar(-16); + mx->fAscent = SkIntToScalar(-16); + mx->fDescent = SkIntToScalar(4); + mx->fBottom = SkIntToScalar(4); + mx->fLeading = 0; + + // FIXME: + mx->fAvgCharWidth = 0; + mx->fXMin = 0; + mx->fXMax = 0; + mx->fXHeight = 0; + } + if (my) + { + my->fTop = SkIntToScalar(-16); + my->fAscent = SkIntToScalar(-16); + my->fDescent = SkIntToScalar(4); + my->fBottom = SkIntToScalar(4); + my->fLeading = 0; + + // FIXME: + my->fAvgCharWidth = 0; + my->fXMin = 0; + my->fXMax = 0; + my->fXHeight = 0; + } +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) +{ + return SkNEW_ARGS(SkScalerContext_Ascender, (desc)); +} + diff --git a/src/ports/SkFontHost_fontconfig.cpp b/src/ports/SkFontHost_fontconfig.cpp new file mode 100644 index 0000000..21fc773 --- /dev/null +++ b/src/ports/SkFontHost_fontconfig.cpp @@ -0,0 +1,381 @@ +/* libs/graphics/ports/SkFontHost_fontconfig.cpp +** +** Copyright 2008, 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. +*/ + +// ----------------------------------------------------------------------------- +// This file provides implementations of the font resolution members of +// SkFontHost by using the fontconfig[1] library. Fontconfig is usually found +// on Linux systems and handles configuration, parsing and caching issues +// involved with enumerating and matching fonts. +// +// [1] http://fontconfig.org +// ----------------------------------------------------------------------------- + +#include <map> +#include <string> + +#include <fontconfig/fontconfig.h> + +#include "SkFontHost.h" +#include "SkStream.h" + +// This is an extern from SkFontHost_FreeType +SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); + +// ----------------------------------------------------------------------------- +// The rest of Skia requires that fonts be identified by a unique unsigned id +// and that we be able to load them given the id. What we actually get from +// fontconfig is the filename of the font so we keep a locked map from +// filenames to fileid numbers and back. +// +// Note that there's also a unique id in the SkTypeface. This is unique over +// both filename and style. Thus we encode that id as (fileid << 8) | style. +// Although truetype fonts can support multiple faces in a single file, at the +// moment Skia doesn't. +// ----------------------------------------------------------------------------- +static SkMutex global_fc_map_lock; +static std::map<std::string, unsigned> global_fc_map; +static std::map<unsigned, std::string> global_fc_map_inverted; +static std::map<uint32_t, SkTypeface *> global_fc_typefaces; +static unsigned global_fc_map_next_id = 0; + +// This is the maximum size of the font cache. +static const unsigned kFontCacheMemoryBudget = 2 * 1024 * 1024; // 2MB + +static unsigned UniqueIdToFileId(unsigned uniqueid) +{ + return uniqueid >> 8; +} + +static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid) +{ + return static_cast<SkTypeface::Style>(uniqueid & 0xff); +} + +static unsigned FileIdAndStyleToUniqueId(unsigned fileid, + SkTypeface::Style style) +{ + SkASSERT((style & 0xff) == style); + return (fileid << 8) | static_cast<int>(style); +} + +// ----------------------------------------------------------------------------- +// Normally we only return exactly the font asked for. In last-resort cases, +// the request is for one of the basic font names "Sans", "Serif" or +// "Monospace". This function tells you whether a given request is for such a +// fallback. +// ----------------------------------------------------------------------------- +static bool IsFallbackFontAllowed(const char* request) +{ + return strcmp(request, "Sans") == 0 || + strcmp(request, "Serif") == 0 || + strcmp(request, "Monospace") == 0; +} + +class FontConfigTypeface : public SkTypeface { +public: + FontConfigTypeface(Style style, uint32_t id) + : SkTypeface(style, id) + { } +}; + +// ----------------------------------------------------------------------------- +// Find a matching font where @type (one of FC_*) is equal to @value. For a +// list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27. +// The variable arguments are a list of triples, just like the first three +// arguments, and must be NULL terminated. +// +// For example, +// FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf", +// NULL); +// ----------------------------------------------------------------------------- +static FcPattern* FontMatch(const char* type, FcType vtype, const void* value, + ...) +{ + va_list ap; + va_start(ap, value); + + FcPattern* pattern = FcPatternCreate(); + const char* family_requested = NULL; + + for (;;) { + FcValue fcvalue; + fcvalue.type = vtype; + switch (vtype) { + case FcTypeString: + fcvalue.u.s = (FcChar8*) value; + break; + case FcTypeInteger: + fcvalue.u.i = (int)(intptr_t)value; + break; + default: + SkASSERT(!"FontMatch unhandled type"); + } + FcPatternAdd(pattern, type, fcvalue, 0); + + if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0) + family_requested = (const char*) value; + + type = va_arg(ap, const char *); + if (!type) + break; + // FcType is promoted to int when passed through ... + vtype = static_cast<FcType>(va_arg(ap, int)); + value = va_arg(ap, const void *); + }; + va_end(ap); + + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + // Font matching: + // CSS often specifies a fallback list of families: + // font-family: a, b, c, serif; + // However, fontconfig will always do its best to find *a* font when asked + // for something so we need a way to tell if the match which it has found is + // "good enough" for us. Otherwise, we can return NULL which gets piped up + // and lets WebKit know to try the next CSS family name. However, fontconfig + // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we + // wish to support that. + // + // Thus, if a specific family is requested we set @family_requested. Then we + // record two strings: the family name after config processing and the + // family name after resolving. If the two are equal, it's a good match. + // + // So consider the case where a user has mapped Arial to Helvetica in their + // config. + // requested family: "Arial" + // post_config_family: "Helvetica" + // post_match_family: "Helvetica" + // -> good match + // + // and for a missing font: + // requested family: "Monaco" + // post_config_family: "Monaco" + // post_match_family: "Times New Roman" + // -> BAD match + // + // However, we special-case fallback fonts; see IsFallbackFontAllowed(). + FcChar8* post_config_family; + FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family); + + FcResult result; + FcPattern* match = FcFontMatch(0, pattern, &result); + if (!match) { + FcPatternDestroy(pattern); + return NULL; + } + + FcChar8* post_match_family; + FcPatternGetString(match, FC_FAMILY, 0, &post_match_family); + const bool family_names_match = + !family_requested ? + true : + strcasecmp((char *)post_config_family, (char *)post_match_family) == 0; + + FcPatternDestroy(pattern); + + if (!family_names_match && !IsFallbackFontAllowed(family_requested)) { + FcPatternDestroy(match); + return NULL; + } + + return match; +} + +// ----------------------------------------------------------------------------- +// Check to see if the filename has already been assigned a fileid and, if so, +// use it. Otherwise, assign one. Return the resulting fileid. +// ----------------------------------------------------------------------------- +static unsigned FileIdFromFilename(const char* filename) +{ + SkAutoMutexAcquire ac(global_fc_map_lock); + + std::map<std::string, unsigned>::const_iterator i = + global_fc_map.find(filename); + if (i == global_fc_map.end()) { + const unsigned fileid = global_fc_map_next_id++; + global_fc_map[filename] = fileid; + global_fc_map_inverted[fileid] = filename; + return fileid; + } else { + return i->second; + } +} + +// static +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char familyName[], + const void* data, size_t bytelength, + SkTypeface::Style style) +{ + const char* resolved_family_name = NULL; + FcPattern* face_match = NULL; + + { + SkAutoMutexAcquire ac(global_fc_map_lock); + FcInit(); + } + + if (familyFace) { + // Here we use the inverted global id map to find the filename from the + // SkTypeface object. Given the filename we can ask fontconfig for the + // familyname of the font. + SkAutoMutexAcquire ac(global_fc_map_lock); + + const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID()); + std::map<unsigned, std::string>::const_iterator i = + global_fc_map_inverted.find(fileid); + if (i == global_fc_map_inverted.end()) + return NULL; + + FcInit(); + face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(), + NULL); + + if (!face_match) + return NULL; + FcChar8* family; + if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) { + FcPatternDestroy(face_match); + return NULL; + } + // At this point, @family is pointing into the @face_match object so we + // cannot release it yet. + + resolved_family_name = reinterpret_cast<char*>(family); + } else if (familyName) { + resolved_family_name = familyName; + } else { + return NULL; + } + + // At this point, we have a resolved_family_name from somewhere + SkASSERT(resolved_family_name); + + const int bold = style & SkTypeface::kBold ? + FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL; + const int italic = style & SkTypeface::kItalic ? + FC_SLANT_ITALIC : FC_SLANT_ROMAN; + FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name, + FC_WEIGHT, FcTypeInteger, bold, + FC_SLANT, FcTypeInteger, italic, + NULL); + if (face_match) + FcPatternDestroy(face_match); + + if (!match) + return NULL; + + FcChar8* filename; + if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) { + FcPatternDestroy(match); + return NULL; + } + // Now @filename is pointing into @match + + const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename)); + const unsigned id = FileIdAndStyleToUniqueId(fileid, style); + SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id)); + FcPatternDestroy(match); + + { + SkAutoMutexAcquire ac(global_fc_map_lock); + global_fc_typefaces[id] = typeface; + } + + return typeface; +} + +// static +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) +{ + SkASSERT(!"SkFontHost::CreateTypefaceFromStream unimplemented"); + return NULL; +} + +// static +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) +{ + SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented"); + return NULL; +} + +// static +bool SkFontHost::ValidFontID(SkFontID uniqueID) { + SkAutoMutexAcquire ac(global_fc_map_lock); + return global_fc_typefaces.find(uniqueID) != global_fc_typefaces.end(); +} + +// static +SkStream* SkFontHost::OpenStream(uint32_t id) +{ + SkAutoMutexAcquire ac(global_fc_map_lock); + const unsigned fileid = UniqueIdToFileId(id); + + std::map<unsigned, std::string>::const_iterator i = + global_fc_map_inverted.find(fileid); + if (i == global_fc_map_inverted.end()) + return NULL; + + return SkNEW_ARGS(SkFILEStream, (i->second.c_str())); +} + +size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, + int32_t* index) { + SkAutoMutexAcquire ac(global_fc_map_lock); + const unsigned fileid = UniqueIdToFileId(fontID); + + std::map<unsigned, std::string>::const_iterator i = + global_fc_map_inverted.find(fileid); + if (i == global_fc_map_inverted.end()) { + return 0; + } + + const std::string& str = i->second; + if (path) { + memcpy(path, str.c_str(), SkMin32(str.size(), length)); + } + if (index) { // TODO: check if we're in a TTC + *index = 0; + } + return str.size(); +} + +void SkFontHost::Serialize(const SkTypeface*, SkWStream*) { + SkASSERT(!"SkFontHost::Serialize unimplemented"); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + SkASSERT(!"SkFontHost::Deserialize unimplemented"); + return NULL; +} + +// static +uint32_t SkFontHost::NextLogicalFont(SkFontID fontID) { + // We don't handle font fallback, WebKit does. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + if (sizeAllocatedSoFar > kFontCacheMemoryBudget) + return sizeAllocatedSoFar - kFontCacheMemoryBudget; + else + return 0; // nothing to do +} diff --git a/src/ports/SkFontHost_gamma_none.cpp b/src/ports/SkFontHost_gamma_none.cpp new file mode 100644 index 0000000..37f190d --- /dev/null +++ b/src/ports/SkFontHost_gamma_none.cpp @@ -0,0 +1,34 @@ +/* Copyright 2008, 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. +*/ + +// ----------------------------------------------------------------------------- +// This is a noop gamma implementation for systems where gamma is already +// corrected, or dealt with in a system wide fashion. For example, on X windows +// one uses the xgamma utility to set the server-wide gamma correction value. +// ----------------------------------------------------------------------------- + +#include "SkFontHost.h" + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) +{ + tables[0] = NULL; + tables[1] = NULL; +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) +{ + return 0; +} + diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp new file mode 100644 index 0000000..e85dff4 --- /dev/null +++ b/src/ports/SkFontHost_linux.cpp @@ -0,0 +1,615 @@ +/* libs/graphics/ports/SkFontHost_android.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 "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkMMapStream.h" +#include "SkOSFile.h" +#include "SkPaint.h" +#include "SkString.h" +#include "SkStream.h" +#include "SkThread.h" +#include "SkTSearch.h" +#include <stdio.h> + +#define FONT_CACHE_MEMORY_BUDGET (1 * 1024 * 1024) + +#ifndef SK_FONT_FILE_PREFIX + #define SK_FONT_FILE_PREFIX "/usr/share/fonts/truetype/msttcorefonts/" +#endif + +SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); + +static void GetFullPathForSysFonts(SkString* full, const char name[]) +{ + full->append(SK_FONT_FILE_PREFIX); + full->append(name); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct FamilyRec; + +/* This guy holds a mapping of a name -> family, used for looking up fonts. + Since it is stored in a stretchy array that doesn't preserve object + semantics, we don't use constructor/destructors, but just have explicit + helpers to manage our internal bookkeeping. + */ +struct NameFamilyPair { + const char* fName; // we own this + FamilyRec* fFamily; // we don't own this, we just reference it + + void construct(const char name[], FamilyRec* family) + { + fName = strdup(name); + fFamily = family; // we don't own this, so just record the referene + } + void destruct() + { + free((char*)fName); + // we don't own family, so just ignore our reference + } +}; + +// we use atomic_inc to grow this for each typeface we create +static int32_t gUniqueFontID; + +// this is the mutex that protects these globals +static SkMutex gFamilyMutex; +static FamilyRec* gFamilyHead; +static SkTDArray<NameFamilyPair> gNameList; + +struct FamilyRec { + FamilyRec* fNext; + SkTypeface* fFaces[4]; + + FamilyRec() + { + fNext = gFamilyHead; + memset(fFaces, 0, sizeof(fFaces)); + gFamilyHead = this; + } +}; + +static SkTypeface* find_best_face(const FamilyRec* family, + SkTypeface::Style style) { + SkTypeface* const* faces = family->fFaces; + + if (faces[style] != NULL) { // exact match + return faces[style]; + } + // look for a matching bold + style = (SkTypeface::Style)(style ^ SkTypeface::kItalic); + if (faces[style] != NULL) { + return faces[style]; + } + // look for the plain + if (faces[SkTypeface::kNormal] != NULL) { + return faces[SkTypeface::kNormal]; + } + // look for anything + for (int i = 0; i < 4; i++) { + if (faces[i] != NULL) { + return faces[i]; + } + } + // should never get here, since the faces list should not be empty + SkASSERT(!"faces list is empty"); + return NULL; +} + +static FamilyRec* find_family(const SkTypeface* member) { + FamilyRec* curr = gFamilyHead; + while (curr != NULL) { + for (int i = 0; i < 4; i++) { + if (curr->fFaces[i] == member) { + return curr; + } + } + curr = curr->fNext; + } + return NULL; +} + +static SkTypeface* find_from_uniqueID(uint32_t uniqueID) { + FamilyRec* curr = gFamilyHead; + while (curr != NULL) { + for (int i = 0; i < 4; i++) { + SkTypeface* face = curr->fFaces[i]; + if (face != NULL && face->uniqueID() == uniqueID) { + return face; + } + } + curr = curr->fNext; + } + return false; +} + +static bool valid_uniqueID(uint32_t uniqueID) { + return find_from_uniqueID(uniqueID) != NULL; +} + +/* Remove reference to this face from its family. If the resulting family + is empty (has no faces), return that family, otherwise return NULL + */ +static FamilyRec* remove_from_family(const SkTypeface* face) { + FamilyRec* family = find_family(face); + SkASSERT(family->fFaces[face->style()] == face); + family->fFaces[face->style()] = NULL; + + for (int i = 0; i < 4; i++) { + if (family->fFaces[i] != NULL) { // family is non-empty + return NULL; + } + } + return family; // return the empty family +} + +// maybe we should make FamilyRec be doubly-linked +static void detach_and_delete_family(FamilyRec* family) { + FamilyRec* curr = gFamilyHead; + FamilyRec* prev = NULL; + + while (curr != NULL) { + FamilyRec* next = curr->fNext; + if (curr == family) { + if (prev == NULL) { + gFamilyHead = next; + } else { + prev->fNext = next; + } + SkDELETE(family); + return; + } + prev = curr; + curr = next; + } + SkASSERT(!"Yikes, couldn't find family in our list to remove/delete"); +} + +static FamilyRec* find_familyrec(const char name[]) { + const NameFamilyPair* list = gNameList.begin(); + int index = SkStrLCSearch(&list[0].fName, gNameList.count(), name, + sizeof(list[0])); + return index >= 0 ? list[index].fFamily : NULL; +} + +static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) { + FamilyRec* rec = find_familyrec(name); + return rec ? find_best_face(rec, style) : NULL; +} + +static SkTypeface* find_typeface(const SkTypeface* familyMember, + SkTypeface::Style style) { + const FamilyRec* family = find_family(familyMember); + return family ? find_best_face(family, style) : NULL; +} + +static void add_name(const char name[], FamilyRec* family) { + SkAutoAsciiToLC tolc(name); + name = tolc.lc(); + + NameFamilyPair* list = gNameList.begin(); + int count = gNameList.count(); + + int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); + + if (index < 0) { + list = gNameList.insert(~index); + list->construct(name, family); + } +} + +static void remove_from_names(FamilyRec* emptyFamily) { +#ifdef SK_DEBUG + for (int i = 0; i < 4; i++) { + SkASSERT(emptyFamily->fFaces[i] == NULL); + } +#endif + + SkTDArray<NameFamilyPair>& list = gNameList; + + // must go backwards when removing + for (int i = list.count() - 1; i >= 0; --i) { + NameFamilyPair* pair = &list[i]; + if (pair->fFamily == emptyFamily) { + pair->destruct(); + list.remove(i); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +class FamilyTypeface : public SkTypeface { +public: + FamilyTypeface(Style style, bool sysFont, FamilyRec* family) + : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) { + fIsSysFont = sysFont; + + SkAutoMutexAcquire ac(gFamilyMutex); + + if (NULL == family) { + family = SkNEW(FamilyRec); + } + family->fFaces[style] = this; + fFamilyRec = family; // just record it so we can return it if asked + } + + virtual ~FamilyTypeface() { + SkAutoMutexAcquire ac(gFamilyMutex); + + // remove us from our family. If the family is now empty, we return + // that and then remove that family from the name list + FamilyRec* family = remove_from_family(this); + if (NULL != family) { + remove_from_names(family); + detach_and_delete_family(family); + } + } + + bool isSysFont() const { return fIsSysFont; } + FamilyRec* getFamily() const { return fFamilyRec; } + // openStream returns a SkStream that has been ref-ed + virtual SkStream* openStream() = 0; + virtual const char* getUniqueString() const = 0; + +private: + FamilyRec* fFamilyRec; // we don't own this, just point to it + bool fIsSysFont; + + typedef SkTypeface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +/* This subclass is just a place holder for when we have no fonts available. + It exists so that our globals (e.g. gFamilyHead) that expect *something* + will not be null. + */ +class EmptyTypeface : public FamilyTypeface { +public: + EmptyTypeface() : INHERITED(SkTypeface::kNormal, true, NULL) {} + + // overrides + virtual SkStream* openStream() { return NULL; } + virtual const char* getUniqueString() const { return NULL; } + +private: + typedef FamilyTypeface INHERITED; +}; + +class StreamTypeface : public FamilyTypeface { +public: + StreamTypeface(Style style, bool sysFont, FamilyRec* family, + SkStream* stream) + : INHERITED(style, sysFont, family) { + stream->ref(); + fStream = stream; + } + virtual ~StreamTypeface() { + fStream->unref(); + } + + // overrides + virtual SkStream* openStream() + { + // openStream returns a refed stream. + fStream->ref(); + return fStream; + } + virtual const char* getUniqueString() const { return NULL; } + +private: + SkStream* fStream; + + typedef FamilyTypeface INHERITED; +}; + +class FileTypeface : public FamilyTypeface { +public: + FileTypeface(Style style, bool sysFont, FamilyRec* family, + const char path[]) + : INHERITED(style, sysFont, family) { + fPath.set(path); + } + + // overrides + virtual SkStream* openStream() + { + SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str())); + + // check for failure + if (stream->getLength() <= 0) { + SkDELETE(stream); + // maybe MMAP isn't supported. try FILE + stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str())); + if (stream->getLength() <= 0) { + SkDELETE(stream); + stream = NULL; + } + } + return stream; + } + + virtual const char* getUniqueString() const { + const char* str = strrchr(fPath.c_str(), '/'); + if (str) { + str += 1; // skip the '/' + } + return str; + } + +private: + SkString fPath; + + typedef FamilyTypeface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool get_name_and_style(const char path[], SkString* name, + SkTypeface::Style* style) { + SkMMAPStream stream(path); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + else { + SkFILEStream stream(path); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + } + + SkDebugf("---- failed to open <%s> as a font\n", path); + return false; +} + +// these globals are assigned (once) by load_system_fonts() +static SkTypeface* gFallBackTypeface; +static FamilyRec* gDefaultFamily; +static SkTypeface* gDefaultNormal; + +static void load_system_fonts() { + // check if we've already be called + if (NULL != gDefaultNormal) { + printf("---- default font %p\n", gDefaultNormal); + return; + } + + SkOSFile::Iter iter(SK_FONT_FILE_PREFIX, ".ttf"); + SkString name; + int count = 0; + + while (iter.next(&name, false)) { + SkString filename; + GetFullPathForSysFonts(&filename, name.c_str()); + + SkString realname; + SkTypeface::Style style = SkTypeface::kNormal; // avoid uninitialized warning + + if (!get_name_and_style(filename.c_str(), &realname, &style)) { + SkDebugf("------ can't load <%s> as a font\n", filename.c_str()); + continue; + } + +// SkDebugf("font: <%s> %d <%s>\n", realname.c_str(), style, filename.c_str()); + + FamilyRec* family = find_familyrec(realname.c_str()); + if (family && family->fFaces[style]) { +// SkDebugf("---- skipping duplicate typeface %s style %d\n", +// realname.c_str(), style); + continue; + } + + // this constructor puts us into the global gFamilyHead llist + FamilyTypeface* tf = SkNEW_ARGS(FileTypeface, + (style, + true, // system-font (cannot delete) + family, // what family to join + filename.c_str()) // filename + ); + + if (NULL == family) { + add_name(realname.c_str(), tf->getFamily()); + } + count += 1; + } + + if (0 == count) { + SkNEW(EmptyTypeface); + } + + // do this after all fonts are loaded. This is our default font, and it + // acts as a sentinel so we only execute load_system_fonts() once + static const char* gDefaultNames[] = { + "Arial", "Verdana", "Times New Roman", NULL + }; + const char** names = gDefaultNames; + while (*names) { + SkTypeface* tf = find_typeface(*names++, SkTypeface::kNormal); + if (tf) { + gDefaultNormal = tf; + break; + } + } + // check if we found *something* + if (NULL == gDefaultNormal) { + if (NULL == gFamilyHead) { + sk_throw(); + } + for (int i = 0; i < 4; i++) { + if ((gDefaultNormal = gFamilyHead->fFaces[i]) != NULL) { + break; + } + } + } + if (NULL == gDefaultNormal) { + sk_throw(); + } + gFallBackTypeface = gDefaultNormal; + gDefaultFamily = find_family(gDefaultNormal); + +// SkDebugf("---- default %p head %p family %p\n", gDefaultNormal, gFamilyHead, gDefaultFamily); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { +#if 0 + const char* name = ((FamilyTypeface*)face)->getUniqueString(); + + stream->write8((uint8_t)face->getStyle()); + + if (NULL == name || 0 == *name) { + stream->writePackedUInt(0); + // SkDebugf("--- fonthost serialize null\n"); + } else { + uint32_t len = strlen(name); + stream->writePackedUInt(len); + stream->write(name, len); + // SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle()); + } +#endif + sk_throw(); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { +#if 0 + load_system_fonts(); + + int style = stream->readU8(); + + int len = stream->readPackedUInt(); + if (len > 0) { + SkString str; + str.resize(len); + stream->read(str.writable_str(), len); + + const FontInitRec* rec = gSystemFonts; + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { + if (strcmp(rec[i].fFileName, str.c_str()) == 0) { + // backup until we hit the fNames + for (int j = i; j >= 0; --j) { + if (rec[j].fNames != NULL) { + return SkFontHost::CreateTypeface(NULL, rec[j].fNames[0], NULL, 0, + (SkTypeface::Style)style); + } + } + } + } + } + return SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, (SkTypeface::Style)style); +#endif + sk_throw(); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char familyName[], + const void* data, size_t bytelength, + SkTypeface::Style style) { + load_system_fonts(); + + SkAutoMutexAcquire ac(gFamilyMutex); + + // clip to legal style bits + style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic); + + SkTypeface* tf = NULL; + + if (NULL != familyFace) { + tf = find_typeface(familyFace, style); + } else if (NULL != familyName) { + // SkDebugf("======= familyName <%s>\n", familyName); + tf = find_typeface(familyName, style); + } + + if (NULL == tf) { + tf = find_best_face(gDefaultFamily, style); + } + + SkSafeRef(tf); + return tf; +} + +bool SkFontHost::ValidFontID(uint32_t fontID) { + SkAutoMutexAcquire ac(gFamilyMutex); + + return valid_uniqueID(fontID); +} + +SkStream* SkFontHost::OpenStream(uint32_t fontID) { + FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID); + SkStream* stream = tf ? tf->openStream() : NULL; + + if (stream && stream->getLength() == 0) { + stream->unref(); + stream = NULL; + } + return stream; +} + +size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, + int32_t* index) { + SkDebugf("SkFontHost::GetFileName unimplemented\n"); + return 0; +} + +uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) { + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { + if (NULL == stream || stream->getLength() <= 0) { + SkDELETE(stream); + return NULL; + } + + SkString name; + SkTypeface::Style style = find_name_and_style(stream, &name); + + return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream)); +} + +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { + SkTypeface* face = NULL; + SkFILEStream* stream = SkNEW_ARGS(SkFILEStream, (path)); + + if (stream->isValid()) { + face = CreateTypefaceFromStream(stream); + } + stream->unref(); + return face; +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { + if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) + return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; + else + return 0; // nothing to do +} + diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp new file mode 100755 index 0000000..6adaf32 --- /dev/null +++ b/src/ports/SkFontHost_mac.cpp @@ -0,0 +1,46 @@ +/* + ** 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. +*/ + + +/* + ** Mac Text API + ** + ** + ** Two text APIs are available on the Mac, ATSUI and CoreText. + ** + ** ATSUI is available on all versions of Mac OS X, but is 32-bit only. + ** + ** The replacement API, CoreText, supports both 32-bit and 64-bit builds + ** but is only available from Mac OS X 10.5 onwards. + ** + ** To maintain support for Mac OS X 10.4, we default to ATSUI in 32-bit + ** builds unless SK_USE_CORETEXT is defined. +*/ +#ifndef SK_USE_CORETEXT + #if TARGET_RT_64_BIT + #define SK_USE_CORETEXT 1 + #else + #define SK_USE_CORETEXT 0 + #endif +#endif + +#if SK_USE_CORETEXT + #include "SkFontHost_mac_coretext.cpp" +#else + #include "SkFontHost_mac_atsui.cpp" +#endif + + diff --git a/src/ports/SkFontHost_mac_atsui.cpp b/src/ports/SkFontHost_mac_atsui.cpp new file mode 100644 index 0000000..5183730 --- /dev/null +++ b/src/ports/SkFontHost_mac_atsui.cpp @@ -0,0 +1,617 @@ +/* + ** 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 <Carbon/Carbon.h> +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkEndian.h" +#include "SkFloatingPoint.h" +#include "SkPaint.h" +#include "SkPoint.h" + +// Give 1MB font cache budget +#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024) + +const char* gDefaultfont = "Arial"; // hard code for now +static SkMutex gFTMutex; + +static inline SkPoint F32PtToSkPoint(const Float32Point p) { + SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) }; + return sp; +} + +static inline uint32_t _rotl(uint32_t v, uint32_t r) { + return (v << r | v >> (32 - r)); +} + +class SkTypeface_Mac : public SkTypeface { +public: + SkTypeface_Mac(SkTypeface::Style style, uint32_t id) + : SkTypeface(style, id) {} +}; + +#pragma mark - + +static uint32_t find_from_name(const char name[]) { + CFStringRef str = CFStringCreateWithCString(NULL, name, + kCFStringEncodingUTF8); + uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault); + CFRelease(str); + return fontID; +} + +static uint32_t find_default_fontID() { + static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" }; + + uint32_t fontID; + for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) { + fontID = find_from_name(gDefaultNames[i]); + if (fontID) { + return fontID; + } + } + sk_throw(); + return 0; +} + +static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) { + uint32_t fontID = 0; + if (NULL != name) { + fontID = find_from_name(name); + } + if (0 == fontID) { + fontID = find_default_fontID(); + } + // we lie (for now) and report that we found the exact style bits + return new SkTypeface_Mac(style, fontID); +} + +#pragma mark - + +class SkScalerContext_Mac : public SkScalerContext { +public: + SkScalerContext_Mac(const SkDescriptor* desc); + virtual ~SkScalerContext_Mac(); + +protected: + virtual unsigned generateGlyphCount() const; + virtual uint16_t generateCharToGlyph(SkUnichar uni); + virtual void generateAdvance(SkGlyph* glyph); + virtual void generateMetrics(SkGlyph* glyph); + virtual void generateImage(const SkGlyph& glyph); + virtual void generatePath(const SkGlyph& glyph, SkPath* path); + virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY); + +private: + ATSUTextLayout fLayout; + ATSUStyle fStyle; + CGColorSpaceRef fGrayColorSpace; + CGAffineTransform fTransform; + + static OSStatus MoveTo(const Float32Point *pt, void *cb); + static OSStatus Line(const Float32Point *pt, void *cb); + static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb); + static OSStatus Close(void *cb); +}; + +void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { + // we only support 2 levels of hinting + SkPaint::Hinting h = rec->getHinting(); + if (SkPaint::kSlight_Hinting == h) { + h = SkPaint::kNo_Hinting; + } else if (SkPaint::kFull_Hinting == h) { + h = SkPaint::kNormal_Hinting; + } + rec->setHinting(h); + + // we don't support LCD text + if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) { + rec->fMaskFormat = SkMask::kA8_Format; + } +} + +SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc) + : SkScalerContext(desc), fLayout(0), fStyle(0) +{ + SkAutoMutexAcquire ac(gFTMutex); + OSStatus err; + + err = ::ATSUCreateStyle(&fStyle); + SkASSERT(0 == err); + + SkMatrix m; + fRec.getSingleMatrix(&m); + + fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]), + SkScalarToFloat(m[SkMatrix::kMSkewX]), + SkScalarToFloat(m[SkMatrix::kMSkewY]), + SkScalarToFloat(m[SkMatrix::kMScaleY]), + SkScalarToFloat(m[SkMatrix::kMTransX]), + SkScalarToFloat(m[SkMatrix::kMTransY])); + + ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing; + switch (fRec.getHinting()) { + case SkPaint::kNo_Hinting: + case SkPaint::kSlight_Hinting: + renderOpts |= kATSStyleNoHinting; + break; + case SkPaint::kNormal_Hinting: + case SkPaint::kFull_Hinting: + renderOpts |= kATSStyleApplyHints; + break; + } + + ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID); + // we put everything in the matrix, so our pt size is just 1.0 + Fixed fixedSize = SK_Fixed1; + static const ATSUAttributeTag tags[] = { + kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag + }; + static const ByteCount sizes[] = { + sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts) + }; + const ATSUAttributeValuePtr values[] = { + &fontID, &fixedSize, &fTransform, &renderOpts + }; + err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags), + tags, sizes, values); + SkASSERT(0 == err); + + err = ::ATSUCreateTextLayout(&fLayout); + SkASSERT(0 == err); + + fGrayColorSpace = ::CGColorSpaceCreateDeviceGray(); +} + +SkScalerContext_Mac::~SkScalerContext_Mac() { + ::CGColorSpaceRelease(fGrayColorSpace); + ::ATSUDisposeTextLayout(fLayout); + ::ATSUDisposeStyle(fStyle); +} + +// man, we need to consider caching this, since it is just dependent on +// fFontID, and not on any of the other settings like matrix or flags +unsigned SkScalerContext_Mac::generateGlyphCount() const { + // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes + uint16_t numGlyphs; + if (SkFontHost::GetTableData(fRec.fFontID, + SkSetFourByteTag('m', 'a', 'x', 'p'), + 4, 2, &numGlyphs) != 2) { + return 0xFFFF; + } + return SkEndian_SwapBE16(numGlyphs); +} + +uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) +{ + SkAutoMutexAcquire ac(gFTMutex); + + OSStatus err; + UniChar achar = uni; + err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1); + err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd); + + ATSLayoutRecord *layoutPtr; + ItemCount count; + ATSGlyphRef glyph; + + err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count); + glyph = layoutPtr->glyphID; + ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr); + return glyph; +} + +static void set_glyph_metrics_on_error(SkGlyph* glyph) { + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + glyph->fWidth = 0; + glyph->fHeight = 0; + glyph->fTop = 0; + glyph->fLeft = 0; + glyph->fAdvanceX = 0; + glyph->fAdvanceY = 0; +} + +void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) { + this->generateMetrics(glyph); +} + +void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) { + GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount); + ATSGlyphScreenMetrics screenMetrics; + ATSGlyphIdealMetrics idealMetrics; + + OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true, + &screenMetrics); + if (noErr != err) { + set_glyph_metrics_on_error(glyph); + return; + } + err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics); + if (noErr != err) { + set_glyph_metrics_on_error(glyph); + return; + } + + if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) { + glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x); + glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y); + } else { + glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x); + glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y); + } + + // specify an extra 1-pixel border, go tive CG room for its antialiasing + // i.e. without this, I was seeing some edges chopped off! + glyph->fWidth = screenMetrics.width + 2; + glyph->fHeight = screenMetrics.height + 2; + glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1; + glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1; +} + +void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) +{ + SkAutoMutexAcquire ac(gFTMutex); + SkASSERT(fLayout); + + sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes()); + CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage, + glyph.fWidth, glyph.fHeight, 8, + glyph.rowBytes(), fGrayColorSpace, + kCGImageAlphaNone); + if (!contextRef) { + SkASSERT(false); + return; + } + + ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0); + ::CGContextSetTextDrawingMode(contextRef, kCGTextFill); + + CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount); + CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID); + CGContextSetFont(contextRef, fontRef); + CGContextSetFontSize(contextRef, 1); + CGContextSetTextMatrix(contextRef, fTransform); + CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft, + glyph.fTop + glyph.fHeight, &glyphID, 1); + + ::CGContextRelease(contextRef); +} + +#if 0 +static void convert_metrics(SkPaint::FontMetrics* dst, + const ATSFontMetrics& src) { + dst->fTop = -SkFloatToScalar(src.ascent); + dst->fAscent = -SkFloatToScalar(src.ascent); + dst->fDescent = SkFloatToScalar(src.descent); + dst->fBottom = SkFloatToScalar(src.descent); + dst->fLeading = SkFloatToScalar(src.leading); +} +#endif + +static void* get_font_table(ATSFontRef fontID, uint32_t tag) { + ByteCount size; + OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size); + if (err) { + return NULL; + } + void* data = sk_malloc_throw(size); + err = ATSFontGetTable(fontID, tag, 0, size, data, &size); + if (err) { + sk_free(data); + data = NULL; + } + return data; +} + +static int get_be16(const void* data, size_t offset) { + const char* ptr = reinterpret_cast<const char*>(data); + uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset); + int n = SkEndian_SwapBE16(value); + // now force it to be signed + return n << 16 >> 16; +} + +#define SFNT_HEAD_UPEM_OFFSET 18 +#define SFNT_HEAD_YMIN_OFFSET 38 +#define SFNT_HEAD_YMAX_OFFSET 42 +#define SFNT_HEAD_STYLE_OFFSET 44 + +#define SFNT_HHEA_ASCENT_OFFSET 4 +#define SFNT_HHEA_DESCENT_OFFSET 6 +#define SFNT_HHEA_LEADING_OFFSET 8 + +static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) { + void* head = get_font_table(font, 'head'); + if (NULL == head) { + return false; + } + void* hhea = get_font_table(font, 'hhea'); + if (NULL == hhea) { + sk_free(head); + return false; + } + + int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET); + int ys[5]; + + ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET); + ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET); + ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET); + ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET); + ys[4] = get_be16(hhea, SFNT_HHEA_LEADING_OFFSET); + + // now do some cleanup, to ensure y[max,min] are really that + if (ys[0] > ys[1]) { + ys[0] = ys[1]; + } + if (ys[3] < ys[2]) { + ys[3] = ys[2]; + } + + for (int i = 0; i < 5; i++) { + pts[i].set(0, SkIntToScalar(ys[i]) / upem); + } + + sk_free(hhea); + sk_free(head); + return true; +} + +void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, + SkPaint::FontMetrics* my) { + SkPoint pts[5]; + + if (!init_vertical_metrics(fRec.fFontID, pts)) { + // these are not as accurate as init_vertical_metrics :( + ATSFontMetrics metrics; + ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault, + &metrics); + pts[0].set(0, -SkFloatToScalar(metrics.ascent)); + pts[1].set(0, -SkFloatToScalar(metrics.ascent)); + pts[2].set(0, -SkFloatToScalar(metrics.descent)); + pts[3].set(0, -SkFloatToScalar(metrics.descent)); + pts[4].set(0, SkFloatToScalar(metrics.leading)); //+ or -? + } + + SkMatrix m; + fRec.getSingleMatrix(&m); + m.mapPoints(pts, 5); + + if (mx) { + mx->fTop = pts[0].fX; + mx->fAscent = pts[1].fX; + mx->fDescent = pts[2].fX; + mx->fBottom = pts[3].fX; + mx->fLeading = pts[4].fX; + // FIXME: + mx->fAvgCharWidth = 0; + mx->fXMin = 0; + mx->fXMax = 0; + mx->fXHeight = 0; + } + if (my) { + my->fTop = pts[0].fY; + my->fAscent = pts[1].fY; + my->fDescent = pts[2].fY; + my->fBottom = pts[3].fY; + my->fLeading = pts[4].fY; + // FIXME: + my->fAvgCharWidth = 0; + my->fXMin = 0; + my->fXMax = 0; + my->fXHeight = 0; + } +} + +void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) +{ + SkAutoMutexAcquire ac(gFTMutex); + OSStatus err,result; + + err = ::ATSUGlyphGetCubicPaths( + fStyle,glyph.fID, + &SkScalerContext_Mac::MoveTo, + &SkScalerContext_Mac::Line, + &SkScalerContext_Mac::Curve, + &SkScalerContext_Mac::Close, + path,&result); + SkASSERT(err == noErr); +} + +OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb) +{ + reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt)); + return noErr; +} + +OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb) +{ + reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt)); + return noErr; +} + +OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1, + const Float32Point *pt2, + const Float32Point *pt3, void *cb) +{ + reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1), + F32PtToSkPoint(*pt2), + F32PtToSkPoint(*pt3)); + return noErr; +} + +OSStatus SkScalerContext_Mac::Close(void *cb) +{ + reinterpret_cast<SkPath*>(cb)->close(); + return noErr; +} + +#pragma mark - + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { + SkASSERT(!"SkFontHost::Serialize unimplemented"); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + SkASSERT(!"SkFontHost::Deserialize unimplemented"); + return NULL; +} + +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { + return NULL; +} + +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { + return NULL; +} + +// static +SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( + uint32_t fontID, bool perGlyphInfo) { + SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented"); + return NULL; +} + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { + return new SkScalerContext_Mac(desc); +} + +uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) { + uint32_t newFontID = find_default_fontID(); + if (newFontID == fontID) { + newFontID = 0; + } + return newFontID; +} + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char familyName[], + const void* data, size_t bytelength, + SkTypeface::Style style) { + // todo: we don't know how to respect style bits + if (NULL == familyName && NULL != familyFace) { + familyFace->ref(); + return const_cast<SkTypeface*>(familyFace); + } else { + return CreateTypeface_(familyName, style); + } +} + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { + if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) + return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; + else + return 0; // nothing to do +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) { + return 0; +} + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) { + tables[0] = NULL; // black gamma (e.g. exp=1.4) + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) +} + +/////////////////////////////////////////////////////////////////////////////// + +struct SkSFNTHeader { + uint32_t fVersion; + uint16_t fNumTables; + uint16_t fSearchRange; + uint16_t fEntrySelector; + uint16_t fRangeShift; +}; + +struct SkSFNTDirEntry { + uint32_t fTag; + uint32_t fChecksum; + uint32_t fOffset; + uint32_t fLength; +}; + +struct SfntHeader { + SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) { + ByteCount size; + if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) { + return; + } + + SkAutoMalloc storage(size); + SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get()); + if (ATSFontGetTableDirectory(fontID, size, header, &size)) { + return; + } + + fCount = SkEndian_SwapBE16(header->fNumTables); + fData = header; + storage.detach(); + } + + ~SfntHeader() { + sk_free(fData); + } + + int count() const { return fCount; } + const SkSFNTDirEntry* entries() const { + return reinterpret_cast<const SkSFNTDirEntry*> + (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader)); + } + +private: + int fCount; + void* fData; +}; + +int SkFontHost::CountTables(SkFontID fontID) { + SfntHeader header(fontID, false); + return header.count(); +} + +int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) { + SfntHeader header(fontID, true); + int count = header.count(); + const SkSFNTDirEntry* entry = header.entries(); + for (int i = 0; i < count; i++) { + tags[i] = SkEndian_SwapBE32(entry[i].fTag); + } + return count; +} + +size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) { + ByteCount size; + if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) { + return 0; + } + return size; +} + +size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag, + size_t offset, size_t length, void* data) { + ByteCount size; + if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) { + return 0; + } + if (offset >= size) { + return 0; + } + if (offset + length > size) { + length = size - offset; + } + return length; +} + diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp new file mode 100644 index 0000000..4b5c36d --- /dev/null +++ b/src/ports/SkFontHost_mac_coretext.cpp @@ -0,0 +1,819 @@ +/* + ** 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 <vector> +#include <Carbon/Carbon.h> + +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkString.h" +#include "SkPaint.h" +#include "SkFloatingPoint.h" + + + + +//============================================================================ +// Constants +//---------------------------------------------------------------------------- +static const SkFontID kSkInvalidFontID = 0; + +static const size_t FONT_CACHE_MEMORY_BUDGET = 1024 * 1024; +static const char FONT_DEFAULT_NAME[] = "Lucida Sans"; + +static const float FONT_CANONICAL_POINTSIZE = 1.0f; + + +//============================================================================ +// Types +//---------------------------------------------------------------------------- +// Native font info +typedef struct { + SkString name; + SkTypeface::Style style; + SkFontID fontID; + CTFontRef fontRef; +} SkNativeFontInfo; + +typedef std::vector<SkNativeFontInfo> SkNativeFontInfoList; +typedef SkNativeFontInfoList::iterator SkNativeFontInfoListIterator; +typedef SkNativeFontInfoList::const_iterator SkNativeFontInfoListConstIterator; + + + + + +//============================================================================ +// Macros +//---------------------------------------------------------------------------- +// Release a CFTypeRef +#ifndef CFSafeRelease +#define CFSafeRelease(_object) \ + do \ + { \ + if ((_object) != NULL) \ + { \ + CFRelease((CFTypeRef) (_object)); \ + (_object) = NULL; \ + } \ + } \ + while (false) +#endif + + + + + +//============================================================================ +// SkNativeFontCache +//---------------------------------------------------------------------------- +#pragma mark - +class SkNativeFontCache { +public: + SkNativeFontCache(void); + virtual ~SkNativeFontCache(void); + + + // Is a font ID valid? + bool IsValid(SkFontID fontID); + + + // Get a font + CTFontRef GetFont(SkFontID fontID); + SkNativeFontInfo GetFontInfo(const SkString &theName, SkTypeface::Style theStyle); + + + // Create a font + SkNativeFontInfo CreateFont(const SkString &theName, SkTypeface::Style theStyle); + + + // Get the font table + static SkNativeFontCache *Get(void); + + +private: + CTFontRef CreateNativeFont(const SkString &name, SkTypeface::Style style); + + +private: + SkNativeFontInfoList mFonts; + SkMutex mMutex; +}; + +SkNativeFontCache::SkNativeFontCache(void) +{ SkAutoMutexAcquire acquireLock(mMutex); + SkNativeFontInfo fontInfo; + + + // Initialise ourselves + // + // SkTypeface uses a uint32_t to identify fonts, however CoreText font references + // are opaque pointers. + // + // To support 64-bit builds, we need a separate index to look up a 64-bit font + // reference from its 32-bit SkFontID. As an ID of 0 is reserved, we insert a + // dummy entry into the cache so we can use the array index as the font ID. + // + // This could be simplified if SkFontID was changed to a intptr_t, and SkTypeface + // returned an SkFontID from uniqueID(). + fontInfo.name = SkString("__SkNativeFontCache__"); + fontInfo.style = SkTypeface::kNormal; + fontInfo.fontID = kSkInvalidFontID; + fontInfo.fontRef = NULL; + + mFonts.push_back(fontInfo); +} + +SkNativeFontCache::~SkNativeFontCache(void) +{ SkAutoMutexAcquire acquireLock(mMutex); + SkNativeFontInfoListIterator theIter; + + + // Clean up + for (theIter = mFonts.begin(); theIter != mFonts.end(); theIter++) + CFSafeRelease(theIter->fontRef); +} + +bool SkNativeFontCache::IsValid(SkFontID fontID) +{ SkAutoMutexAcquire acquireLock(mMutex); + bool isValid; + + + // Check the ID + isValid = (fontID >= 1 && fontID < mFonts.size()); + return(isValid); +} + +CTFontRef SkNativeFontCache::GetFont(SkFontID fontID) +{ SkAutoMutexAcquire acquireLock(mMutex); + + + // Validate our parameters + SkASSERT(fontID >= 1 && fontID < mFonts.size()); + + + // Get the font + return(mFonts.at(fontID).fontRef); +} + +SkNativeFontInfo SkNativeFontCache::GetFontInfo(const SkString &theName, SkTypeface::Style theStyle) +{ SkAutoMutexAcquire acquireLock(mMutex); + SkNativeFontInfo fontInfo; + SkNativeFontInfoListIterator theIter; + + + // Validate our parameters + SkASSERT(!theName.isEmpty()); + + + // Get the state we need + fontInfo.style = SkTypeface::kNormal; + fontInfo.fontID = kSkInvalidFontID; + fontInfo.fontRef = NULL; + + + // Get the font + for (theIter = mFonts.begin(); theIter != mFonts.end(); theIter++) + { + if (theIter->name == theName && theIter->style == theStyle) + return(*theIter); + } + + return(fontInfo); +} + +SkNativeFontInfo SkNativeFontCache::CreateFont(const SkString &theName, SkTypeface::Style theStyle) +{ SkAutoMutexAcquire acquireLock(mMutex); + SkNativeFontInfo fontInfo; + + + // Validate our parameters + SkASSERT(!theName.isEmpty()); + + + // Create the font + fontInfo.name = theName; + fontInfo.style = theStyle; + fontInfo.fontID = mFonts.size(); + fontInfo.fontRef = CreateNativeFont(theName, theStyle); + + mFonts.push_back(fontInfo); + return(fontInfo); +} + +SkNativeFontCache *SkNativeFontCache::Get(void) +{ static SkNativeFontCache sInstance; + + + // Get the instance + // + // We use a local static for well-defined static initialisation order. + return(&sInstance); +} + +/////////////////////////////////////////////////////////////////////////// +CTFontRef SkNativeFontCache::CreateNativeFont(const SkString &theName, SkTypeface::Style theStyle) +{ CFMutableDictionaryRef cfAttributes, cfTraits; + CFNumberRef cfFontTraits; + CTFontSymbolicTraits ctFontTraits; + CTFontDescriptorRef ctFontDesc; + CFStringRef cfFontName; + CTFontRef ctFont; + + + // Get the state we need + ctFontDesc = NULL; + ctFont = NULL; + ctFontTraits = 0; + + if (theStyle & SkTypeface::kBold) + ctFontTraits |= kCTFontBoldTrait; + + if (theStyle & SkTypeface::kItalic) + ctFontTraits |= kCTFontItalicTrait; + + + // Create the font info + cfFontName = CFStringCreateWithCString(NULL, theName.c_str(), kCFStringEncodingUTF8); + cfFontTraits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits); + cfAttributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + cfTraits = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + + // Create the font + // + // Fonts are scaled using the Sk matrix, so we always request a font + // at a canonical size FONT_CANONICAL_POINTSIZE + if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) + { + CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits); + + CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName); + CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits); + + ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes); + if (ctFontDesc != NULL) + ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, FONT_CANONICAL_POINTSIZE, NULL); + + } + + + // Clean up + CFSafeRelease(cfFontName); + CFSafeRelease(cfFontTraits); + CFSafeRelease(cfAttributes); + CFSafeRelease(cfTraits); + CFSafeRelease(ctFontDesc); + + return(ctFont); +} + + + + + +//============================================================================ +// SkTypeface_Mac +//---------------------------------------------------------------------------- +#pragma mark - +class SkTypeface_Mac : public SkTypeface { +public: + SkTypeface_Mac(SkTypeface::Style style, uint32_t fontID); +}; + + +SkTypeface_Mac::SkTypeface_Mac(SkTypeface::Style style, uint32_t fontID) + : SkTypeface(style, fontID) +{ +} + + + + + +//============================================================================ +// SkScalerContext_Mac +//---------------------------------------------------------------------------- +#pragma mark - +class SkScalerContext_Mac : public SkScalerContext { +public: + SkScalerContext_Mac(const SkDescriptor* desc); + virtual ~SkScalerContext_Mac(void); + + +protected: + unsigned generateGlyphCount(void) const; + uint16_t generateCharToGlyph(SkUnichar uni); + void generateAdvance(SkGlyph* glyph); + void generateMetrics(SkGlyph* glyph); + void generateImage(const SkGlyph& glyph); + void generatePath( const SkGlyph& glyph, SkPath* path); + void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY); + + +private: + static void CTPathElement(void *info, const CGPathElement *element); + + +private: + CGColorSpaceRef mColorSpace; + CGAffineTransform mTransform; + + CTFontRef mFont; + uint16_t mGlyphCount; +}; + +SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc) + : SkScalerContext(desc) +{ CFIndex numGlyphs; + CTFontRef ctFont; + SkMatrix skMatrix; + + + + // Get the state we need + fRec.getSingleMatrix(&skMatrix); + + ctFont = SkNativeFontCache::Get()->GetFont(fRec.fFontID); + numGlyphs = CTFontGetGlyphCount(ctFont); + SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF); + + + // Initialise ourselves + mColorSpace = CGColorSpaceCreateDeviceGray(); + const float inv = 1.0f / FONT_CANONICAL_POINTSIZE; + mTransform = CGAffineTransformMake(SkScalarToFloat(skMatrix[SkMatrix::kMScaleX]) * inv, + -SkScalarToFloat(skMatrix[SkMatrix::kMSkewY]) * inv, + -SkScalarToFloat(skMatrix[SkMatrix::kMSkewX]) * inv, + SkScalarToFloat(skMatrix[SkMatrix::kMScaleY]) * inv, + SkScalarToFloat(skMatrix[SkMatrix::kMTransX]) * inv, + SkScalarToFloat(skMatrix[SkMatrix::kMTransY]) * inv); + + mFont = CTFontCreateCopyWithAttributes(ctFont, 0.0, &mTransform, NULL); + mGlyphCount = (uint16_t) numGlyphs; +} + +SkScalerContext_Mac::~SkScalerContext_Mac(void) +{ + + // Clean up + CFSafeRelease(mColorSpace); + CFSafeRelease(mFont); +} + +unsigned SkScalerContext_Mac::generateGlyphCount(void) const +{ + return(mGlyphCount); +} + +uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) +{ CGGlyph cgGlyph; + UniChar theChar; + + + // Validate our parameters and state + SkASSERT(uni <= 0x0000FFFF); + SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t)); + + + // Get the glyph + theChar = (UniChar) uni; + + if (!CTFontGetGlyphsForCharacters(mFont, &theChar, &cgGlyph, 1)) + cgGlyph = 0; + + return(cgGlyph); +} + +void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) +{ + this->generateMetrics(glyph); +} + +void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) +{ CGSize theAdvance; + CGRect theBounds; + CGGlyph cgGlyph; + + + + // Get the state we need + cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount); + + CTFontGetBoundingRectsForGlyphs(mFont, kCTFontDefaultOrientation, &cgGlyph, &theBounds, 1); + CTFontGetAdvancesForGlyphs( mFont, kCTFontDefaultOrientation, &cgGlyph, &theAdvance, 1); + + + + // Adjust the bounds + // + // CTFontGetBoundingRectsForGlyphs ignores the font transform, so we need + // to transform the bounding box ourselves. + // + // The bounds are also expanded by 1 pixel, to give CG room for anti-aliasing. + theBounds = CGRectInset(theBounds, -1, -1); + + + + // Get the metrics + glyph->zeroMetrics(); + glyph->fAdvanceX = SkFloatToFixed(theAdvance.width); + glyph->fAdvanceY = -SkFloatToFixed(theAdvance.height); + glyph->fWidth = sk_float_round2int(theBounds.size.width); + glyph->fHeight = sk_float_round2int(theBounds.size.height); + glyph->fTop = -sk_float_round2int(CGRectGetMaxY(theBounds)); + glyph->fLeft = sk_float_round2int(CGRectGetMinX(theBounds)); +} + +void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) +{ CGContextRef cgContext; + CGGlyph cgGlyph; + CGFontRef cgFont; + + // Get the state we need + sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes()); + + cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount); + cgFont = CTFontCopyGraphicsFont(mFont, NULL); + cgContext = CGBitmapContextCreate( glyph.fImage, glyph.fWidth, glyph.fHeight, 8, + glyph.rowBytes(), mColorSpace, kCGImageAlphaNone); + + // Draw the glyph + if (cgFont != NULL && cgContext != NULL) { + CGContextSetGrayFillColor( cgContext, 1.0, 1.0); + CGContextSetTextDrawingMode(cgContext, kCGTextFill); + CGContextSetFont( cgContext, cgFont); + CGContextSetFontSize( cgContext, FONT_CANONICAL_POINTSIZE); + CGContextSetTextMatrix( cgContext, mTransform); + CGContextShowGlyphsAtPoint( cgContext, -glyph.fLeft, glyph.fTop + glyph.fHeight, &cgGlyph, 1); + } + + // Clean up + CFSafeRelease(cgFont); + CFSafeRelease(cgContext); +} + +void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) { + CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount); + CGPathRef cgPath = CTFontCreatePathForGlyph(mFont, cgGlyph, NULL); + + path->reset(); + if (cgPath != NULL) { + CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement); + CFRelease(cgPath); + } +} + +void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, + SkPaint::FontMetrics* my) { + CGRect theBounds = CTFontGetBoundingBox(mFont); + + SkPaint::FontMetrics theMetrics; + theMetrics.fTop = -CGRectGetMaxY(theBounds); + theMetrics.fAscent = -CTFontGetAscent(mFont); + theMetrics.fDescent = CTFontGetDescent(mFont); + theMetrics.fBottom = -CGRectGetMinY(theBounds); + theMetrics.fLeading = CTFontGetLeading(mFont); + theMetrics.fAvgCharWidth = CGRectGetWidth(theBounds); + theMetrics.fXMin = CGRectGetMinX(theBounds); + theMetrics.fXMax = CGRectGetMaxX(theBounds); + theMetrics.fXHeight = CTFontGetXHeight(mFont); + +#if 0 + SkASSERT(theMetrics.fTop <= 0.0); + SkASSERT(theMetrics.fAscent <= 0.0); + SkASSERT(theMetrics.fDescent >= 0.0); + SkASSERT(theMetrics.fBottom >= 0.0); + SkASSERT(theMetrics.fLeading >= 0.0); + SkASSERT(theMetrics.fAvgCharWidth >= 0.0); + SkASSERT(theMetrics.fXMin <= 0.0); + SkASSERT(theMetrics.fXMax > 0.0); + SkASSERT(theMetrics.fXHeight >= 0.0); +#endif + + if (mx != NULL) { + *mx = theMetrics; + } + if (my != NULL) { + *my = theMetrics; + } +} + +void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) +{ SkPath *skPath = (SkPath *) info; + + + // Process the path element + switch (element->type) { + case kCGPathElementMoveToPoint: + skPath->moveTo( element->points[0].x, -element->points[0].y); + break; + + case kCGPathElementAddLineToPoint: + skPath->lineTo( element->points[0].x, -element->points[0].y); + break; + + case kCGPathElementAddQuadCurveToPoint: + skPath->quadTo( element->points[0].x, -element->points[0].y, + element->points[1].x, -element->points[1].y); + break; + + case kCGPathElementAddCurveToPoint: + skPath->cubicTo(element->points[0].x, -element->points[0].y, + element->points[1].x, -element->points[1].y, + element->points[2].x, -element->points[2].y); + break; + + case kCGPathElementCloseSubpath: + skPath->close(); + break; + + default: + SkASSERT("Unknown path element!"); + break; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////// +#pragma mark - + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char familyName[], + const void* data, size_t bytelength, + SkTypeface::Style style) +{ SkTypeface *theTypeface; + SkNativeFontCache *fontTable; + SkNativeFontInfo fontInfo; + SkString fontName; + + + // Get the state we need + fontName = SkString(familyName); + fontTable = SkNativeFontCache::Get(); + + + // Clone an existing typeface + // TODO: only clone if style matches the familyFace's style... + if (familyName == NULL && familyFace != NULL) + { + familyFace->ref(); + return(const_cast<SkTypeface*>(familyFace)); + } + + + if (fontName.isEmpty()) { + fontName.set(FONT_DEFAULT_NAME); + } + // Get the native font + fontInfo = fontTable->GetFontInfo(fontName, style); + if (fontInfo.fontID == kSkInvalidFontID) + fontInfo = fontTable->CreateFont(fontName, style); + + + // Create the typeface + theTypeface = new SkTypeface_Mac(fontInfo.style, fontInfo.fontID); + return(theTypeface); +} + +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) +{ +// SkASSERT(!"SkFontHost::CreateTypefaceFromStream unimplemented"); + return SkFontHost::CreateTypeface(NULL, NULL, NULL, NULL, SkTypeface::kNormal); +} + +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) +{ +// SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented"); + return SkFontHost::CreateTypeface(NULL, NULL, NULL, NULL, SkTypeface::kNormal); +} + +// static +SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( + uint32_t fontID, bool perGlyphInfo) { + SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented"); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////// + +bool SkFontHost::ValidFontID(SkFontID uniqueID) +{ + + // Check the font ID + return(SkNativeFontCache::Get()->IsValid(uniqueID)); +} + +SkStream* SkFontHost::OpenStream(SkFontID uniqueID) +{ + SkASSERT(!"SkFontHost::OpenStream unimplemented"); + return(NULL); +} + +size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) +{ + SkASSERT(!"SkFontHost::GetFileName unimplemented"); + return(0); +} + +/////////////////////////////////////////////////////////////////////////// + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) +{ + SkASSERT(!"SkFontHost::Serialize unimplemented"); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) +{ + SkASSERT(!"SkFontHost::Deserialize unimplemented"); + return(NULL); +} + +/////////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) +{ + return new SkScalerContext_Mac(desc); +} + +uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) +{ SkTypeface *typeFace; + uint32_t newFontID; + + + // Get the state we need + newFontID = kSkInvalidFontID; + typeFace = CreateTypeface(NULL, FONT_DEFAULT_NAME, NULL, 0, SkTypeface::kNormal); + + if (typeFace == NULL) + return(0); + + + // Get the next font + // + // When we're passed in the default font, we've reached the end. + newFontID = typeFace->uniqueID(); + if (newFontID == fontID) + newFontID = 0; + + + // Clean up + typeFace->unref(); + + return(newFontID); +} + +void SkFontHost::FilterRec(SkScalerContext::Rec* rec) +{ + // we only support 2 levels of hinting + SkPaint::Hinting h = rec->getHinting(); + if (SkPaint::kSlight_Hinting == h) { + h = SkPaint::kNo_Hinting; + } else if (SkPaint::kFull_Hinting == h) { + h = SkPaint::kNormal_Hinting; + } + rec->setHinting(h); + + // we don't support LCD text + if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) { + rec->fMaskFormat = SkMask::kA8_Format; + } +} + +/////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) +{ + if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) + return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; + else + return 0; // nothing to do +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) +{ + return 0; +} + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) +{ + tables[0] = NULL; // black gamma (e.g. exp=1.4) + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) +} + +/////////////////////////////////////////////////////////////////////////// + +int SkFontHost::CountTables(SkFontID fontID) +{ int numTables; + CFArrayRef cfArray; + CTFontRef ctFont; + + + // Get the state we need + ctFont = SkNativeFontCache::Get()->GetFont(fontID); + cfArray = CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions); + numTables = 0; + + + // Get the table count + if (cfArray != NULL) + { + numTables = CFArrayGetCount(cfArray); + CFSafeRelease(cfArray); + } + + return(numTables); +} + +int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) +{ int n, numTables; + CFArrayRef cfArray; + CTFontRef ctFont; + + + // Get the state we need + ctFont = SkNativeFontCache::Get()->GetFont(fontID); + cfArray = CTFontCopyAvailableTables(ctFont, kCTFontTableOptionNoOptions); + numTables = 0; + + + // Get the table tags + if (cfArray != NULL) + { + numTables = CFArrayGetCount(cfArray); + for (n = 0; n < numTables; n++) + tags[n] = (SkFontTableTag) ((uintptr_t) CFArrayGetValueAtIndex(cfArray, n)); + + CFSafeRelease(cfArray); + } + + return(numTables); +} + +size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) +{ size_t theSize; + CTFontRef ctFont; + CFDataRef cfData; + + + // Get the state we need + ctFont = SkNativeFontCache::Get()->GetFont(fontID); + cfData = CTFontCopyTable(ctFont, (CTFontTableTag) tag, kCTFontTableOptionNoOptions); + theSize = 0; + + + // Get the data size + if (cfData != NULL) + { + theSize = CFDataGetLength(cfData); + CFSafeRelease(cfData); + } + + return(theSize); +} + +size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag, + size_t offset, size_t length, void* data) +{ size_t theSize; + CTFontRef ctFont; + CFDataRef cfData; + + + // Get the state we need + ctFont = SkNativeFontCache::Get()->GetFont(fontID); + cfData = CTFontCopyTable(ctFont, (CTFontTableTag) tag, kCTFontTableOptionNoOptions); + theSize = 0; + + + // Get the data + if (cfData != NULL) + theSize = CFDataGetLength(cfData); + + if (offset >= theSize) + return 0; + + if ((offset + length) > theSize) + length = theSize - offset; + + memcpy(data, CFDataGetBytePtr(cfData) + offset, length); + return(length); +} + + + + diff --git a/src/ports/SkFontHost_none.cpp b/src/ports/SkFontHost_none.cpp new file mode 100644 index 0000000..d56c94a --- /dev/null +++ b/src/ports/SkFontHost_none.cpp @@ -0,0 +1,101 @@ +/* Copyright 2006-2008, 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 "SkFontHost.h" + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char famillyName[], + const void* data, size_t bytelength, + SkTypeface::Style style) { + SkASSERT(!"SkFontHost::FindTypeface unimplemented"); + return NULL; +} + +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream*) { + SkASSERT(!"SkFontHost::CreateTypeface unimplemented"); + return NULL; +} + +SkTypeface* SkFontHost::CreateTypefaceFromFile(char const*) { + SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented"); + return NULL; +} + +// static +SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( + uint32_t fontID, bool perGlyphInfo) { + SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented"); + return NULL; +} + +void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkFontHost::ValidFontID(uint32_t uniqueID) { + SkASSERT(!"SkFontHost::ResolveTypeface unimplemented"); + return false; +} + +SkStream* SkFontHost::OpenStream(uint32_t uniqueID) { + SkASSERT(!"SkFontHost::OpenStream unimplemented"); + return NULL; +} + +size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, + int32_t* index) { + SkDebugf("SkFontHost::GetFileName unimplemented\n"); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { + SkASSERT(!"SkFontHost::Serialize unimplemented"); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + SkASSERT(!"SkFontHost::Deserialize unimplemented"); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { + SkASSERT(!"SkFontHost::CreateScalarContext unimplemented"); + return NULL; +} + +uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) { + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { + return 0; // nothing to do (change me if you want to limit the font cache) +} + +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) { + return 0; +} + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) { + tables[0] = NULL; // black gamma (e.g. exp=1.4) + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) +} + diff --git a/src/ports/SkFontHost_simple.cpp b/src/ports/SkFontHost_simple.cpp new file mode 100644 index 0000000..6df6c44 --- /dev/null +++ b/src/ports/SkFontHost_simple.cpp @@ -0,0 +1,659 @@ +/* + * 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. + */ + +#include "SkFontHost.h" +#include "SkDescriptor.h" +#include "SkMMapStream.h" +#include "SkPaint.h" +#include "SkString.h" +#include "SkStream.h" +#include "SkThread.h" +#include "SkTSearch.h" +#include <stdio.h> + +#define FONT_CACHE_MEMORY_BUDGET (768 * 1024) + +#define SK_FONT_FILE_PREFIX "/skimages/" + +SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name); + +static void GetFullPathForSysFonts(SkString* full, const char name[]) { + full->set(SK_FONT_FILE_PREFIX); + full->append(name); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct FamilyRec; + +/* This guy holds a mapping of a name -> family, used for looking up fonts. + Since it is stored in a stretchy array that doesn't preserve object + semantics, we don't use constructor/destructors, but just have explicit + helpers to manage our internal bookkeeping. +*/ +struct NameFamilyPair { + const char* fName; // we own this + FamilyRec* fFamily; // we don't own this, we just reference it + + void construct(const char name[], FamilyRec* family) { + fName = strdup(name); + fFamily = family; // we don't own this, so just record the referene + } + + void destruct() { + free((char*)fName); + // we don't own family, so just ignore our reference + } +}; + +// we use atomic_inc to grow this for each typeface we create +static int32_t gUniqueFontID; + +// this is the mutex that protects these globals +static SkMutex gFamilyMutex; +static FamilyRec* gFamilyHead; +static SkTDArray<NameFamilyPair> gNameList; + +struct FamilyRec { + FamilyRec* fNext; + SkTypeface* fFaces[4]; + + FamilyRec() + { + fNext = gFamilyHead; + memset(fFaces, 0, sizeof(fFaces)); + gFamilyHead = this; + } +}; + +static SkTypeface* find_best_face(const FamilyRec* family, + SkTypeface::Style style) { + SkTypeface* const* faces = family->fFaces; + + if (faces[style] != NULL) { // exact match + return faces[style]; + } + // look for a matching bold + style = (SkTypeface::Style)(style ^ SkTypeface::kItalic); + if (faces[style] != NULL) { + return faces[style]; + } + // look for the plain + if (faces[SkTypeface::kNormal] != NULL) { + return faces[SkTypeface::kNormal]; + } + // look for anything + for (int i = 0; i < 4; i++) { + if (faces[i] != NULL) { + return faces[i]; + } + } + // should never get here, since the faces list should not be empty + SkASSERT(!"faces list is empty"); + return NULL; +} + +static FamilyRec* find_family(const SkTypeface* member) { + FamilyRec* curr = gFamilyHead; + while (curr != NULL) { + for (int i = 0; i < 4; i++) { + if (curr->fFaces[i] == member) { + return curr; + } + } + curr = curr->fNext; + } + return NULL; +} + +/* Returns the matching typeface, or NULL. If a typeface is found, its refcnt + is not modified. + */ +static SkTypeface* find_from_uniqueID(uint32_t uniqueID) { + FamilyRec* curr = gFamilyHead; + while (curr != NULL) { + for (int i = 0; i < 4; i++) { + SkTypeface* face = curr->fFaces[i]; + if (face != NULL && face->uniqueID() == uniqueID) { + return face; + } + } + curr = curr->fNext; + } + return NULL; +} + +/* Remove reference to this face from its family. If the resulting family + is empty (has no faces), return that family, otherwise return NULL +*/ +static FamilyRec* remove_from_family(const SkTypeface* face) { + FamilyRec* family = find_family(face); + SkASSERT(family->fFaces[face->style()] == face); + family->fFaces[face->style()] = NULL; + + for (int i = 0; i < 4; i++) { + if (family->fFaces[i] != NULL) { // family is non-empty + return NULL; + } + } + return family; // return the empty family +} + +// maybe we should make FamilyRec be doubly-linked +static void detach_and_delete_family(FamilyRec* family) { + FamilyRec* curr = gFamilyHead; + FamilyRec* prev = NULL; + + while (curr != NULL) { + FamilyRec* next = curr->fNext; + if (curr == family) { + if (prev == NULL) { + gFamilyHead = next; + } else { + prev->fNext = next; + } + SkDELETE(family); + return; + } + prev = curr; + curr = next; + } + SkASSERT(!"Yikes, couldn't find family in our list to remove/delete"); +} + +static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) { + NameFamilyPair* list = gNameList.begin(); + int count = gNameList.count(); + + int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); + + if (index >= 0) { + return find_best_face(list[index].fFamily, style); + } + return NULL; +} + +static SkTypeface* find_typeface(const SkTypeface* familyMember, + SkTypeface::Style style) { + const FamilyRec* family = find_family(familyMember); + return family ? find_best_face(family, style) : NULL; +} + +static void add_name(const char name[], FamilyRec* family) { + SkAutoAsciiToLC tolc(name); + name = tolc.lc(); + + NameFamilyPair* list = gNameList.begin(); + int count = gNameList.count(); + + int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0])); + + if (index < 0) { + list = gNameList.insert(~index); + list->construct(name, family); + } +} + +static void remove_from_names(FamilyRec* emptyFamily) +{ +#ifdef SK_DEBUG + for (int i = 0; i < 4; i++) { + SkASSERT(emptyFamily->fFaces[i] == NULL); + } +#endif + + SkTDArray<NameFamilyPair>& list = gNameList; + + // must go backwards when removing + for (int i = list.count() - 1; i >= 0; --i) { + NameFamilyPair* pair = &list[i]; + if (pair->fFamily == emptyFamily) { + pair->destruct(); + list.remove(i); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +class FamilyTypeface : public SkTypeface { +public: + FamilyTypeface(Style style, bool sysFont, SkTypeface* familyMember) + : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) { + fIsSysFont = sysFont; + + SkAutoMutexAcquire ac(gFamilyMutex); + + FamilyRec* rec = NULL; + if (familyMember) { + rec = find_family(familyMember); + SkASSERT(rec); + } else { + rec = SkNEW(FamilyRec); + } + rec->fFaces[style] = this; + } + + virtual ~FamilyTypeface() { + SkAutoMutexAcquire ac(gFamilyMutex); + + // remove us from our family. If the family is now empty, we return + // that and then remove that family from the name list + FamilyRec* family = remove_from_family(this); + if (NULL != family) { + remove_from_names(family); + detach_and_delete_family(family); + } + } + + bool isSysFont() const { return fIsSysFont; } + + virtual SkStream* openStream() = 0; + virtual const char* getUniqueString() const = 0; + virtual const char* getFilePath() const = 0; + +private: + bool fIsSysFont; + + typedef SkTypeface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class StreamTypeface : public FamilyTypeface { +public: + StreamTypeface(Style style, bool sysFont, SkTypeface* familyMember, + SkStream* stream) + : INHERITED(style, sysFont, familyMember) { + SkASSERT(stream); + stream->ref(); + fStream = stream; + } + virtual ~StreamTypeface() { + fStream->unref(); + } + + // overrides + virtual SkStream* openStream() { + // we just ref our existing stream, since the caller will call unref() + // when they are through + fStream->ref(); + return fStream; + } + virtual const char* getUniqueString() const { return NULL; } + virtual const char* getFilePath() const { return NULL; } + +private: + SkStream* fStream; + + typedef FamilyTypeface INHERITED; +}; + +class FileTypeface : public FamilyTypeface { +public: + FileTypeface(Style style, bool sysFont, SkTypeface* familyMember, + const char path[]) + : INHERITED(style, sysFont, familyMember) { + SkString fullpath; + + if (sysFont) { + GetFullPathForSysFonts(&fullpath, path); + path = fullpath.c_str(); + } + fPath.set(path); + } + + // overrides + virtual SkStream* openStream() { + SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str())); + + // check for failure + if (stream->getLength() <= 0) { + SkDELETE(stream); + // maybe MMAP isn't supported. try FILE + stream = SkNEW_ARGS(SkFILEStream, (fPath.c_str())); + if (stream->getLength() <= 0) { + SkDELETE(stream); + stream = NULL; + } + } + return stream; + } + virtual const char* getUniqueString() const { + const char* str = strrchr(fPath.c_str(), '/'); + if (str) { + str += 1; // skip the '/' + } + return str; + } + virtual const char* getFilePath() const { + return fPath.c_str(); + } + +private: + SkString fPath; + + typedef FamilyTypeface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool get_name_and_style(const char path[], SkString* name, + SkTypeface::Style* style, bool isExpected) { + SkString fullpath; + GetFullPathForSysFonts(&fullpath, path); + + SkMMAPStream stream(fullpath.c_str()); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + else { + SkFILEStream stream(fullpath.c_str()); + if (stream.getLength() > 0) { + *style = find_name_and_style(&stream, name); + return true; + } + } + + if (isExpected) { + SkDebugf("---- failed to open <%s> as a font\n", fullpath.c_str()); + } + return false; +} + +// used to record our notion of the pre-existing fonts +struct FontInitRec { + const char* fFileName; + const char* const* fNames; // null-terminated list +}; + +static const char* gSansNames[] = { + "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL +}; + +static const char* gSerifNames[] = { + "serif", "times", "times new roman", "palatino", "georgia", "baskerville", + "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL +}; + +static const char* gMonoNames[] = { + "monospace", "courier", "courier new", "monaco", NULL +}; + +// deliberately empty, but we use the address to identify fallback fonts +static const char* gFBNames[] = { NULL }; + +/* Fonts must be grouped by family, with the first font in a family having the + list of names (even if that list is empty), and the following members having + null for the list. The names list must be NULL-terminated +*/ +static const FontInitRec gSystemFonts[] = { + { "samplefont.ttf", gSansNames }, +}; + +#define DEFAULT_NAMES gSansNames + +// these globals are assigned (once) by load_system_fonts() +static FamilyRec* gDefaultFamily; +static SkTypeface* gDefaultNormal; + +/* This is sized conservatively, assuming that it will never be a size issue. + It will be initialized in load_system_fonts(), and will be filled with the + fontIDs that can be used for fallback consideration, in sorted order (sorted + meaning element[0] should be used first, then element[1], etc. When we hit + a fontID==0 in the array, the list is done, hence our allocation size is + +1 the total number of possible system fonts. Also see NextLogicalFont(). + */ +static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1]; + +/* Called once (ensured by the sentinel check at the beginning of our body). + Initializes all the globals, and register the system fonts. + */ +static void load_system_fonts() { + // check if we've already be called + if (NULL != gDefaultNormal) { + return; + } + + const FontInitRec* rec = gSystemFonts; + SkTypeface* firstInFamily = NULL; + int fallbackCount = 0; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { + // if we're the first in a new family, clear firstInFamily + if (rec[i].fNames != NULL) { + firstInFamily = NULL; + } + + SkString name; + SkTypeface::Style style; + + // we expect all the fonts, except the "fallback" fonts + bool isExpected = (rec[i].fNames != gFBNames); + if (!get_name_and_style(rec[i].fFileName, &name, &style, isExpected)) { + continue; + } + + SkTypeface* tf = SkNEW_ARGS(FileTypeface, + (style, + true, // system-font (cannot delete) + firstInFamily, // what family to join + rec[i].fFileName) // filename + ); + + if (rec[i].fNames != NULL) { + // see if this is one of our fallback fonts + if (rec[i].fNames == gFBNames) { + // SkDebugf("---- adding %s as fallback[%d] fontID %d\n", + // rec[i].fFileName, fallbackCount, tf->uniqueID()); + gFallbackFonts[fallbackCount++] = tf->uniqueID(); + } + + firstInFamily = tf; + FamilyRec* family = find_family(tf); + const char* const* names = rec[i].fNames; + + // record the default family if this is it + if (names == DEFAULT_NAMES) { + gDefaultFamily = family; + } + // add the names to map to this family + while (*names) { + add_name(*names, family); + names += 1; + } + } + } + + // do this after all fonts are loaded. This is our default font, and it + // acts as a sentinel so we only execute load_system_fonts() once + gDefaultNormal = find_best_face(gDefaultFamily, SkTypeface::kNormal); + // now terminate our fallback list with the sentinel value + gFallbackFonts[fallbackCount] = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { + const char* name = ((FamilyTypeface*)face)->getUniqueString(); + + stream->write8((uint8_t)face->style()); + + if (NULL == name || 0 == *name) { + stream->writePackedUInt(0); +// SkDebugf("--- fonthost serialize null\n"); + } else { + uint32_t len = strlen(name); + stream->writePackedUInt(len); + stream->write(name, len); +// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style()); + } +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + load_system_fonts(); + + int style = stream->readU8(); + + int len = stream->readPackedUInt(); + if (len > 0) { + SkString str; + str.resize(len); + stream->read(str.writable_str(), len); + + const FontInitRec* rec = gSystemFonts; + for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { + if (strcmp(rec[i].fFileName, str.c_str()) == 0) { + // backup until we hit the fNames + for (int j = i; j >= 0; --j) { + if (rec[j].fNames != NULL) { + return SkFontHost::CreateTypeface(NULL, + rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style); + } + } + } + } + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char familyName[], + const void* data, size_t bytelength, + SkTypeface::Style style) { + load_system_fonts(); + + SkAutoMutexAcquire ac(gFamilyMutex); + + // clip to legal style bits + style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic); + + SkTypeface* tf = NULL; + + if (NULL != familyFace) { + tf = find_typeface(familyFace, style); + } else if (NULL != familyName) { +// SkDebugf("======= familyName <%s>\n", familyName); + tf = find_typeface(familyName, style); + } + + if (NULL == tf) { + tf = find_best_face(gDefaultFamily, style); + } + + // we ref(), since the symantic is to return a new instance + tf->ref(); + return tf; +} + +bool SkFontHost::ValidFontID(uint32_t fontID) { + SkAutoMutexAcquire ac(gFamilyMutex); + + return find_from_uniqueID(fontID) != NULL; +} + +SkStream* SkFontHost::OpenStream(uint32_t fontID) { + SkAutoMutexAcquire ac(gFamilyMutex); + + FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID); + SkStream* stream = tf ? tf->openStream() : NULL; + + if (stream && stream->getLength() == 0) { + stream->unref(); + stream = NULL; + } + return stream; +} + +// static +SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( + uint32_t fontID, bool perGlyphInfo) { + SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented"); + return NULL; +} + +size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, + int32_t* index) { + SkAutoMutexAcquire ac(gFamilyMutex); + + FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID); + const char* src = tf ? tf->getFilePath() : NULL; + + if (src) { + size_t size = strlen(src); + if (path) { + memcpy(path, src, SkMin32(size, length)); + } + if (index) { + *index = 0; // we don't have collections (yet) + } + return size; + } else { + return 0; + } +} + +uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) { + load_system_fonts(); + + /* First see if fontID is already one of our fallbacks. If so, return + its successor. If fontID is not in our list, then return the first one + in our list. Note: list is zero-terminated, and returning zero means + we have no more fonts to use for fallbacks. + */ + const uint32_t* list = gFallbackFonts; + for (int i = 0; list[i] != 0; i++) { + if (list[i] == fontID) { + return list[i+1]; + } + } + return list[0]; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { + if (NULL == stream || stream->getLength() <= 0) { + return NULL; + } + + SkString name; + SkTypeface::Style style = find_name_and_style(stream, &name); + + return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream)); +} + +SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { + SkStream* stream = SkNEW_ARGS(SkMMAPStream, (path)); + SkTypeface* face = SkFontHost::CreateTypefaceFromStream(stream); + // since we created the stream, we let go of our ref() here + stream->unref(); + return face; +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { + if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) + return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; + else + return 0; // nothing to do +} + diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp new file mode 100644 index 0000000..c2e43fa --- /dev/null +++ b/src/ports/SkFontHost_win.cpp @@ -0,0 +1,746 @@ +/*
+ ** 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 "SkString.h"
+//#include "SkStream.h"
+
+#include "SkFontHost.h"
+#include "SkDescriptor.h"
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkStream.h"
+#include "SkThread.h"
+
+#ifdef WIN32
+#include "windows.h"
+#include "tchar.h"
+
+// client3d has to undefine this for now
+#define CAN_USE_LOGFONT_NAME
+
+using namespace skia_advanced_typeface_metrics_utils;
+
+static SkMutex gFTMutex;
+
+static const uint16_t BUFFERSIZE = (16384 - 32);
+static uint8_t glyphbuf[BUFFERSIZE];
+
+// Give 1MB font cache budget
+#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024)
+
+static inline FIXED SkFixedToFIXED(SkFixed x) {
+ return *(FIXED*)(&x);
+}
+
+static inline FIXED SkScalarToFIXED(SkScalar x) {
+ return SkFixedToFIXED(SkScalarToFixed(x));
+}
+
+static SkTypeface::Style GetFontStyle(const LOGFONT& lf) {
+ int style = SkTypeface::kNormal;
+ if (lf.lfWeight == FW_SEMIBOLD || lf.lfWeight == FW_DEMIBOLD || lf.lfWeight == FW_BOLD)
+ style |= SkTypeface::kBold;
+ if (lf.lfItalic)
+ style |= SkTypeface::kItalic;
+
+ return (SkTypeface::Style)style;
+}
+
+// have to do this because SkTypeface::SkTypeface() is protected
+class LogFontTypeface : public SkTypeface {
+private:
+ static SkMutex gMutex;
+ static LogFontTypeface* gHead;
+ static int32_t gCurrId;
+
+ LogFontTypeface* fNext;
+ LOGFONT fLogFont;
+
+public:
+
+ LogFontTypeface(Style style, const LOGFONT& logFont) :
+ SkTypeface(style, sk_atomic_inc(&gCurrId)+1), // 0 id is reserved so add 1
+ fLogFont(logFont)
+ {
+ SkAutoMutexAcquire am(gMutex);
+ fNext = gHead;
+ gHead = this;
+ }
+
+ const LOGFONT& logFont() const { return fLogFont; }
+
+ virtual ~LogFontTypeface() {
+ SkAutoMutexAcquire am(gMutex);
+ if (gHead == this) {
+ gHead = fNext;
+ return;
+ }
+
+ LogFontTypeface* prev = gHead;
+ SkASSERT(prev);
+ while (prev->fNext != this) {
+ prev = prev->fNext;
+ SkASSERT(prev);
+ }
+ prev->fNext = fNext;
+ }
+
+ static LogFontTypeface* FindById(uint32_t id){
+ SkASSERT(gHead);
+ LogFontTypeface* curr = gHead;
+ while (curr->uniqueID() != id) {
+ curr = curr->fNext;
+ SkASSERT(curr);
+ }
+ return curr;
+ }
+
+ static LogFontTypeface* FindByLogFont(const LOGFONT& lf)
+ {
+ LogFontTypeface* curr = gHead;
+ while (curr && memcmp(&curr->fLogFont, &lf, sizeof(LOGFONT))) {
+ curr = curr->fNext;
+ }
+ return curr;
+ }
+};
+
+LogFontTypeface* LogFontTypeface::gHead;
+int32_t LogFontTypeface::gCurrId;
+SkMutex LogFontTypeface::gMutex;
+
+static const LOGFONT& get_default_font() {
+ static LOGFONT gDefaultFont;
+ // don't hardcode on Windows, Win2000, XP, Vista, and international all have different default
+ // and the user could change too
+
+
+// lfMessageFont is garbage on my XP, so skip for now
+#if 0
+ if (gDefaultFont.lfFaceName[0] != 0) {
+ return gDefaultFont;
+ }
+
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
+
+ //memcpy(&gDefaultFont, &(ncm.lfMessageFont), sizeof(LOGFONT));
+#endif
+
+ return gDefaultFont;
+}
+
+static SkTypeface* CreateTypeface_(const LOGFONT& lf) {
+
+ LogFontTypeface* ptypeface = LogFontTypeface::FindByLogFont(lf);
+
+ if (NULL == ptypeface) {
+ SkTypeface::Style style = GetFontStyle(lf);
+ ptypeface = new LogFontTypeface(style, lf);
+ } else {
+ ptypeface->ref();
+ }
+ return ptypeface;
+}
+
+uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
+ // Zero means that we don't have any fallback fonts for this fontID.
+ // This function is implemented on Android, but doesn't have much
+ // meaning here.
+ return 0;
+}
+
+class SkScalerContext_Windows : public SkScalerContext {
+public:
+ SkScalerContext_Windows(const SkDescriptor* desc);
+ virtual ~SkScalerContext_Windows();
+
+protected:
+ virtual unsigned generateGlyphCount() const;
+ virtual uint16_t generateCharToGlyph(SkUnichar uni);
+ virtual void generateAdvance(SkGlyph* glyph);
+ virtual void generateMetrics(SkGlyph* glyph);
+ virtual void generateImage(const SkGlyph& glyph);
+ virtual void generatePath(const SkGlyph& glyph, SkPath* path);
+ virtual void generateLineHeight(SkPoint* ascent, SkPoint* descent);
+ virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+ //virtual SkDeviceContext getDC() {return ddc;}
+private:
+ LOGFONT lf;
+ MAT2 mat22;
+ HDC ddc;
+ HFONT savefont;
+ HFONT font;
+};
+
+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc) : SkScalerContext(desc), ddc(0), font(0), savefont(0) {
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ lf = LogFontTypeface::FindById(fRec.fFontID)->logFont();
+
+ mat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
+ mat22.eM12 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
+ mat22.eM21 = SkScalarToFIXED(fRec.fPost2x2[1][0]);
+ mat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);
+
+ ddc = ::CreateCompatibleDC(NULL);
+ SetBkMode(ddc, TRANSPARENT);
+
+ // Scaling by the DPI is inconsistent with how Skia draws elsewhere
+ //SkScalar height = -(fRec.fTextSize * GetDeviceCaps(ddc, LOGPIXELSY) / 72);
+ SkScalar height = -fRec.fTextSize;
+ lf.lfHeight = SkScalarRound(height);
+ font = CreateFontIndirect(&lf);
+ savefont = (HFONT)SelectObject(ddc, font);
+}
+
+SkScalerContext_Windows::~SkScalerContext_Windows() {
+ if (ddc) {
+ ::SelectObject(ddc, savefont);
+ ::DeleteDC(ddc);
+ ddc = NULL;
+ }
+ if (font) {
+ ::DeleteObject(font);
+ }
+}
+
+unsigned SkScalerContext_Windows::generateGlyphCount() const {
+ return 0xFFFF;
+ // return fFace->num_glyphs;
+}
+
+uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
+ uint16_t index = 0;
+ // TODO(ctguil): Support values larger than 16bits.
+ WCHAR c = SkToU16(uni);
+ GetGlyphIndicesW(ddc, &c, 1, &index, 0);
+ return index;
+}
+
+void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
+ this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
+
+ SkASSERT(ddc);
+
+ GLYPHMETRICS gm;
+ memset(&gm, 0, sizeof(gm));
+
+ glyph->fRsbDelta = 0;
+ glyph->fLsbDelta = 0;
+
+ // Note: need to use GGO_GRAY8_BITMAP instead of GGO_METRICS because GGO_METRICS returns a smaller
+ // BlackBlox; we need the bigger one in case we need the image. fAdvance is the same.
+ uint32_t ret = GetGlyphOutlineW(ddc, glyph->getGlyphID(0), GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &mat22);
+
+ if (GDI_ERROR != ret) {
+ if (ret == 0) {
+ // for white space, ret is zero and gmBlackBoxX, gmBlackBoxY are 1 incorrectly!
+ gm.gmBlackBoxX = gm.gmBlackBoxY = 0;
+ }
+ glyph->fWidth = gm.gmBlackBoxX;
+ glyph->fHeight = gm.gmBlackBoxY;
+ glyph->fTop = SkToS16(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY);
+ glyph->fLeft = SkToS16(gm.gmptGlyphOrigin.x);
+ glyph->fAdvanceX = SkIntToFixed(gm.gmCellIncX);
+ glyph->fAdvanceY = -SkIntToFixed(gm.gmCellIncY);
+ } else {
+ glyph->fWidth = 0;
+ }
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generateMetrics: id:%d, w=%d, h=%d, font:%s, fh:%d\n", glyph->fID, glyph->fWidth, glyph->fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+}
+
+void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) {
+// Note: This code was borrowed from generateLineHeight, which has a note
+// stating that it may be incorrect.
+ if (!(mx || my))
+ return;
+
+ SkASSERT(ddc);
+
+ OUTLINETEXTMETRIC otm;
+
+ uint32_t ret = GetOutlineTextMetrics(ddc, sizeof(otm), &otm);
+ if (sizeof(otm) != ret) {
+ return;
+ }
+
+ if (mx) {
+ mx->fTop = -SkIntToScalar(otm.otmTextMetrics.tmAscent); // Actually a long.
+ mx->fAscent = -SkIntToScalar(otm.otmAscent);
+ mx->fDescent = -SkIntToScalar(otm.otmDescent);
+ mx->fBottom = SkIntToScalar(otm.otmTextMetrics.tmDescent); // Long
+ mx->fLeading = SkIntToScalar(otm.otmTextMetrics.tmInternalLeading
+ + otm.otmTextMetrics.tmExternalLeading); // Long
+ }
+
+ if (my) {
+ my->fTop = -SkIntToScalar(otm.otmTextMetrics.tmAscent); // Actually a long.
+ my->fAscent = -SkIntToScalar(otm.otmAscent);
+ my->fDescent = -SkIntToScalar(otm.otmDescent);
+ my->fBottom = SkIntToScalar(otm.otmTextMetrics.tmDescent); // Long
+ my->fLeading = SkIntToScalar(otm.otmTextMetrics.tmInternalLeading
+ + otm.otmTextMetrics.tmExternalLeading); // Long
+ }
+}
+
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ SkASSERT(ddc);
+
+ GLYPHMETRICS gm;
+ memset(&gm, 0, sizeof(gm));
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generateImage: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+
+ uint32_t bytecount = 0;
+ uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, 0, NULL, &mat22);
+ if (GDI_ERROR != total_size && total_size > 0) {
+ uint8_t *pBuff = new uint8_t[total_size];
+ if (NULL != pBuff) {
+ total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_GRAY8_BITMAP | GGO_GLYPH_INDEX, &gm, total_size, pBuff, &mat22);
+
+ SkASSERT(total_size != GDI_ERROR);
+
+ SkASSERT(glyph.fWidth == gm.gmBlackBoxX);
+ SkASSERT(glyph.fHeight == gm.gmBlackBoxY);
+
+ uint8_t* dst = (uint8_t*)glyph.fImage;
+ uint32_t pitch = (gm.gmBlackBoxX + 3) & ~0x3;
+ if (pitch != glyph.rowBytes()) {
+ SkASSERT(false); // glyph.fImage has different rowsize!?
+ }
+
+ for (int32_t y = gm.gmBlackBoxY - 1; y >= 0; y--) {
+ uint8_t* src = pBuff + pitch * y;
+
+ for (uint32_t x = 0; x < gm.gmBlackBoxX; x++) {
+ if (*src > 63) {
+ *dst = 0xFF;
+ }
+ else {
+ *dst = *src << 2; // scale to 0-255
+ }
+ dst++;
+ src++;
+ bytecount++;
+ }
+ memset(dst, 0, glyph.rowBytes() - glyph.fWidth);
+ dst += glyph.rowBytes() - glyph.fWidth;
+ }
+
+ delete[] pBuff;
+ }
+ }
+
+ SkASSERT(GDI_ERROR != total_size && total_size >= 0);
+
+}
+
+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
+
+ SkAutoMutexAcquire ac(gFTMutex);
+
+ SkASSERT(&glyph && path);
+ SkASSERT(ddc);
+
+ path->reset();
+
+#if 0
+ char buf[1024];
+ sprintf(buf, "generatePath: id:%d, w=%d, h=%d, font:%s,fh:%d\n", glyph.fID, glyph.fWidth, glyph.fHeight, lf.lfFaceName, lf.lfHeight);
+ OutputDebugString(buf);
+#endif
+
+ GLYPHMETRICS gm;
+ uint32_t total_size = GetGlyphOutlineW(ddc, glyph.fID, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, BUFFERSIZE, glyphbuf, &mat22);
+
+ if (GDI_ERROR != total_size) {
+
+ const uint8_t* cur_glyph = glyphbuf;
+ const uint8_t* end_glyph = glyphbuf + total_size;
+
+ while(cur_glyph < end_glyph) {
+ const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph;
+
+ const uint8_t* end_poly = cur_glyph + th->cb;
+ const uint8_t* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER);
+
+ path->moveTo(SkFixedToScalar(*(SkFixed*)(&th->pfxStart.x)), SkFixedToScalar(*(SkFixed*)(&th->pfxStart.y)));
+
+ while(cur_poly < end_poly) {
+ const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly;
+
+ if (pc->wType == TT_PRIM_LINE) {
+ for (uint16_t i = 0; i < pc->cpfx; i++) {
+ path->lineTo(SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].x)), SkFixedToScalar(*(SkFixed*)(&pc->apfx[i].y)));
+ }
+ }
+
+ if (pc->wType == TT_PRIM_QSPLINE) {
+ for (uint16_t u = 0; u < pc->cpfx - 1; u++) { // Walk through points in spline
+ POINTFX pnt_b = pc->apfx[u]; // B is always the current point
+ POINTFX pnt_c = pc->apfx[u+1];
+
+ if (u < pc->cpfx - 2) { // If not on last spline, compute C
+ pnt_c.x = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.x), *(SkFixed*)(&pnt_c.x)));
+ pnt_c.y = SkFixedToFIXED(SkFixedAve(*(SkFixed*)(&pnt_b.y), *(SkFixed*)(&pnt_c.y)));
+ }
+
+ path->quadTo(SkFixedToScalar(*(SkFixed*)(&pnt_b.x)), SkFixedToScalar(*(SkFixed*)(&pnt_b.y)), SkFixedToScalar(*(SkFixed*)(&pnt_c.x)), SkFixedToScalar(*(SkFixed*)(&pnt_c.y)));
+ }
+ }
+ cur_poly += sizeof(uint16_t) * 2 + sizeof(POINTFX) * pc->cpfx;
+ }
+ cur_glyph += th->cb;
+ path->close();
+ }
+ }
+ else {
+ SkASSERT(false);
+ }
+ //char buf[1024];
+ //sprintf(buf, "generatePath: count:%d\n", count);
+ //OutputDebugString(buf);
+}
+
+
+// Note: not sure this is the correct implementation
+void SkScalerContext_Windows::generateLineHeight(SkPoint* ascent, SkPoint* descent) {
+
+ SkASSERT(ddc);
+
+ OUTLINETEXTMETRIC otm;
+
+ uint32_t ret = GetOutlineTextMetrics(ddc, sizeof(otm), &otm);
+
+ if (sizeof(otm) == ret) {
+ if (ascent)
+ ascent->iset(0, otm.otmAscent);
+ if (descent)
+ descent->iset(0, otm.otmDescent);
+ }
+
+ return;
+}
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkASSERT(!"SkFontHost::Deserialize unimplemented");
+ return NULL;
+}
+
+static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
+ // Initialize the MAT2 structure to the identify transformation matrix.
+ static const MAT2 mat2 = {SkScalarToFIXED(1), SkScalarToFIXED(0),
+ SkScalarToFIXED(0), SkScalarToFIXED(1)};
+ int flags = GGO_METRICS | GGO_GLYPH_INDEX;
+ GLYPHMETRICS gm;
+ if (GDI_ERROR == GetGlyphOutline(hdc, gId, flags, &gm, 0, NULL, &mat2)) {
+ return false;
+ }
+ SkASSERT(advance);
+ *advance = gm.gmCellIncX;
+ return true;
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+ uint32_t fontID, bool perGlyphInfo) {
+ SkAutoMutexAcquire ac(gFTMutex);
+ LogFontTypeface* rec = LogFontTypeface::FindById(fontID);
+ LOGFONT lf = rec->logFont();
+ SkAdvancedTypefaceMetrics* info = NULL;
+
+ HDC hdc = CreateCompatibleDC(NULL);
+ HFONT font = CreateFontIndirect(&lf);
+ HFONT savefont = (HFONT)SelectObject(hdc, font);
+ HFONT designFont = NULL;
+
+ // To request design units, create a logical font whose height is specified
+ // as unitsPerEm.
+ OUTLINETEXTMETRIC otm;
+ if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm) ||
+ !GetTextFace(hdc, LF_FACESIZE, lf.lfFaceName)) {
+ goto Error;
+ }
+ lf.lfHeight = -SkToS32(otm.otmEMSquare);
+ designFont = CreateFontIndirect(&lf);
+ SelectObject(hdc, designFont);
+ if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
+ goto Error;
+ }
+
+ info = new SkAdvancedTypefaceMetrics;
+#ifdef UNICODE
+ // Get the buffer size needed first.
+ size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
+ 0, NULL, NULL);
+ // Allocate a buffer (str_len already has terminating null accounted for).
+ char *familyName = new char[str_len];
+ // Now actually convert the string.
+ WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len,
+ NULL, NULL);
+ info->fFontName.set(familyName);
+ delete [] familyName;
+#else
+ info->fFontName.set(lf.lfFaceName);
+#endif
+
+ if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {
+ info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+ } else {
+ info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
+ }
+ info->fEmSize = otm.otmEMSquare;
+ info->fMultiMaster = false;
+
+ info->fStyle = 0;
+ // If this bit is clear the font is a fixed pitch font.
+ if (!(otm.otmTextMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+ }
+ if (otm.otmTextMetrics.tmItalic) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+ }
+ // Setting symbolic style by default for now.
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+ if (otm.otmTextMetrics.tmPitchAndFamily & FF_ROMAN) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+ } else if (otm.otmTextMetrics.tmPitchAndFamily & FF_SCRIPT) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+ }
+
+ // The main italic angle of the font, in tenths of a degree counterclockwise
+ // from vertical.
+ info->fItalicAngle = otm.otmItalicAngle / 10;
+ info->fAscent = SkToS16(otm.otmTextMetrics.tmAscent);
+ info->fDescent = SkToS16(-otm.otmTextMetrics.tmDescent);
+ // TODO(ctguil): Use alternate cap height calculation.
+ // MSDN says otmsCapEmHeight is not support but it is returning a value on
+ // my Win7 box.
+ info->fCapHeight = otm.otmsCapEmHeight;
+ info->fBBox =
+ SkIRect::MakeLTRB(otm.otmrcFontBox.left, otm.otmrcFontBox.top,
+ otm.otmrcFontBox.right, otm.otmrcFontBox.bottom);
+
+ // 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;
+ char stem_chars[] = {'i', 'I', '!', '1'};
+ for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
+ ABC abcWidths;
+ if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
+ int16_t width = abcWidths.abcB;
+ if (width > 0 && width < min_width) {
+ min_width = width;
+ info->fStemV = min_width;
+ }
+ }
+ }
+
+ // If bit 1 is set, the font may not be embedded in a document.
+ // If bit 1 is clear, the font can be embedded.
+ // If bit 2 is set, the embedding is read-only.
+ if (otm.otmfsType & 0x1) {
+ info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+ } else if (perGlyphInfo) {
+ info->fGlyphWidths.reset(
+ getAdvanceData(hdc, SHRT_MAX, &getWidthAdvance));
+ }
+
+Error:
+ SelectObject(hdc, savefont);
+ DeleteObject(designFont);
+ DeleteObject(font);
+ DeleteDC(hdc);
+
+ return info;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+
+ //Should not be used on Windows, keep linker happy
+ SkASSERT(false);
+ return CreateTypeface_(get_default_font());
+}
+
+SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
+ SkAutoMutexAcquire ac(gFTMutex);
+ LogFontTypeface* rec = LogFontTypeface::FindById(uniqueID);
+
+ HDC hdc = ::CreateCompatibleDC(NULL);
+ HFONT font = CreateFontIndirect(&rec->logFont());
+ HFONT savefont = (HFONT)SelectObject(hdc, font);
+
+ size_t bufferSize = GetFontData(hdc, 0, 0, NULL, 0);
+ SkMemoryStream* stream = new SkMemoryStream(bufferSize);
+ if (!GetFontData(hdc, 0, 0, (void*)stream->getMemoryBase(), bufferSize)) {
+ delete stream;
+ stream = NULL;
+ }
+
+ SelectObject(hdc, savefont);
+ DeleteObject(font);
+ DeleteDC(hdc);
+
+ return stream;
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+ return SkNEW_ARGS(SkScalerContext_Windows, (desc));
+}
+
+/** Return the closest matching typeface given either an existing family
+ (specified by a typeface in that family) or by a familyName, and a
+ requested style.
+ 1) If familyFace is null, use famillyName.
+ 2) If famillyName is null, use familyFace.
+ 3) If both are null, return the default font that best matches style
+ This MUST not return NULL.
+ */
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+ const char familyName[],
+ const void* data, size_t bytelength,
+ SkTypeface::Style style) {
+
+ static SkTypeface* gDefaultTypeface;
+ SkAutoMutexAcquire ac(gFTMutex);
+
+#ifndef CAN_USE_LOGFONT_NAME
+ familyName = NULL;
+ familyFace = NULL;
+#endif
+
+ // clip to legal style bits
+ style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
+
+ SkTypeface* tf = NULL;
+ if (NULL == familyFace && NULL == familyName) {
+ LOGFONT lf = get_default_font();
+ lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+ lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
+ // hack until we figure out if SkTypeface should cache this itself
+ if (style == SkTypeface::kNormal) {
+ if (NULL == gDefaultTypeface) {
+ gDefaultTypeface = CreateTypeface_(lf);
+ }
+ tf = gDefaultTypeface;
+ tf->ref();
+ } else {
+ tf = CreateTypeface_(lf);
+ }
+ } else {
+#ifdef CAN_USE_LOGFONT_NAME
+ LOGFONT lf;
+ if (NULL != familyFace) {
+ uint32_t id = familyFace->uniqueID();
+ LogFontTypeface* rec = LogFontTypeface::FindById(id);
+ if (!rec) {
+ SkASSERT(false);
+ lf = get_default_font();
+ }
+ else {
+ lf = rec->logFont();
+ }
+ }
+ else {
+ memset(&lf, 0, sizeof(LOGFONT));
+
+ lf.lfHeight = -11; // default
+ lf.lfQuality = PROOF_QUALITY;
+ lf.lfCharSet = DEFAULT_CHARSET;
+
+#ifdef UNICODE
+ // Get the buffer size needed first.
+ size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
+ -1, NULL, 0);
+ // Allocate a buffer (str_len already has terminating null
+ // accounted for).
+ wchar_t *wideFamilyName = new wchar_t[str_len];
+ // Now actually convert the string.
+ ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
+ wideFamilyName, str_len);
+ ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE);
+ delete [] wideFamilyName;
+#else
+ ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE);
+#endif
+ lf.lfFaceName[LF_FACESIZE-1] = '\0';
+ }
+
+ // use the style desired
+ lf.lfWeight = (style & SkTypeface::kBold) != 0 ? FW_BOLD : FW_NORMAL ;
+ lf.lfItalic = ((style & SkTypeface::kItalic) != 0);
+ tf = CreateTypeface_(lf);
+#endif
+ }
+
+ if (NULL == tf) {
+ tf = CreateTypeface_(get_default_font());
+ }
+ return tf;
+}
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
+ if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
+ return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
+ else
+ return 0; // nothing to do
+}
+
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
+ return 0;
+}
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
+ tables[0] = NULL; // black gamma (e.g. exp=1.4)
+ tables[1] = NULL; // white gamma (e.g. exp= 1/1.4)
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+ printf("SkFontHost::CreateTypefaceFromFile unimplemented");
+ return NULL;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+ // We don't control the hinting nor ClearType settings here
+ rec->setHinting(SkPaint::kNormal_Hinting);
+ if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) {
+ rec->fMaskFormat = SkMask::kA8_Format;
+ }
+}
+
+#endif // WIN32
diff --git a/src/ports/SkHarfBuzzFont.cpp b/src/ports/SkHarfBuzzFont.cpp new file mode 100644 index 0000000..bb229a1 --- /dev/null +++ b/src/ports/SkHarfBuzzFont.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2009 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 "SkHarfBuzzFont.h" +#include "SkFontHost.h" +#include "SkPaint.h" +#include "SkPath.h" + +// HB_Fixed is a 26.6 fixed point format. +static inline HB_Fixed SkScalarToHarfbuzzFixed(SkScalar value) { +#ifdef SK_SCALAR_IS_FLOAT + return static_cast<HB_Fixed>(value * 64); +#else + // convert .16 to .6 + return value >> (16 - 6); +#endif +} + +static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, + hb_uint32 length, HB_Glyph* glyphs, + hb_uint32* glyphsSize, HB_Bool isRTL) { + SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData); + SkPaint paint; + + paint.setTypeface(font->getTypeface()); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), + reinterpret_cast<uint16_t*>(glyphs)); + + // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our + // |glyphs| array needs to be converted. + for (int i = numGlyphs - 1; i >= 0; --i) { + uint16_t value; + // We use a memcpy to avoid breaking strict aliasing rules. + memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(uint16_t)); + glyphs[i] = value; + } + + *glyphsSize = numGlyphs; + return 1; +} + +static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, + hb_uint32 numGlyphs, HB_Fixed* advances, int flags) { + SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + SkAutoMalloc storage(numGlyphs * (sizeof(SkScalar) + sizeof(uint16_t))); + SkScalar* scalarWidths = reinterpret_cast<SkScalar*>(storage.get()); + uint16_t* glyphs16 = reinterpret_cast<uint16_t*>(scalarWidths + numGlyphs); + + // convert HB 32bit glyphs to skia's 16bit + for (hb_uint32 i = 0; i < numGlyphs; ++i) { + glyphs16[i] = SkToU16(glyphs[i]); + } + paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarWidths); + + for (hb_uint32 i = 0; i < numGlyphs; ++i) { + advances[i] = SkScalarToHarfbuzzFixed(scalarWidths[i]); + } +} + +static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, + hb_uint32 length) { + SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData); + SkPaint paint; + + paint.setTypeface(font->getTypeface()); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + return paint.containsText(characters, length * sizeof(uint16_t)); +} + +static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, + hb_uint32 index, HB_Fixed* xPos, HB_Fixed* yPos, + hb_uint32* resultingNumPoints) { + SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + if (flags & HB_ShaperFlag_UseDesignMetrics) { + paint.setHinting(SkPaint::kNo_Hinting); + } + + SkPath path; + uint16_t glyph16 = SkToU16(glyph); + paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path); + int numPoints = path.countPoints(); + if (index >= numPoints) { + return HB_Err_Invalid_SubTable; + } + + SkPoint pt = path.getPoint(index); + *xPos = SkScalarToHarfbuzzFixed(pt.fX); + *yPos = SkScalarToHarfbuzzFixed(pt.fY); + *resultingNumPoints = numPoints; + + return HB_Err_Ok; +} + +static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, + HB_GlyphMetrics* metrics) { + SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData); + SkPaint paint; + + font->setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + SkScalar width; + SkRect bounds; + uint16_t glyph16 = SkToU16(glyph); + paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); + + metrics->x = SkScalarToHarfbuzzFixed(bounds.fLeft); + metrics->y = SkScalarToHarfbuzzFixed(bounds.fTop); + metrics->width = SkScalarToHarfbuzzFixed(bounds.width()); + metrics->height = SkScalarToHarfbuzzFixed(bounds.height()); + + metrics->xOffset = SkScalarToHarfbuzzFixed(width); + // We can't actually get the |y| correct because Skia doesn't export + // the vertical advance. However, nor we do ever render vertical text at + // the moment so it's unimportant. + metrics->yOffset = 0; +} + +static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) +{ + SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(hbFont->userData); + SkPaint paint; + SkPaint::FontMetrics skiaMetrics; + + font->setupPaint(&paint); + paint.getFontMetrics(&skiaMetrics); + + switch (metric) { + case HB_FontAscent: + return SkScalarToHarfbuzzFixed(-skiaMetrics.fAscent); + default: + SkDebugf("--- unknown harfbuzz metric enum %d\n", metric); + return 0; + } +} + +static HB_FontClass gSkHarfBuzzFontClass = { + stringToGlyphs, + glyphsToAdvances, + canRender, + getOutlinePoint, + getGlyphMetrics, + getFontMetric, +}; + +const HB_FontClass& SkHarfBuzzFont::GetFontClass() { + return gSkHarfBuzzFontClass; +} + +HB_Error SkHarfBuzzFont::GetFontTableFunc(void* voidface, const HB_Tag tag, + HB_Byte* buffer, HB_UInt* len) { + SkHarfBuzzFont* font = reinterpret_cast<SkHarfBuzzFont*>(voidface); + uint32_t uniqueID = SkTypeface::UniqueID(font->getTypeface()); + + const size_t tableSize = SkFontHost::GetTableSize(uniqueID, tag); + if (!tableSize) { + return HB_Err_Invalid_Argument; + } + // If Harfbuzz specified a NULL buffer then it's asking for the size. + if (!buffer) { + *len = tableSize; + return HB_Err_Ok; + } + + if (*len < tableSize) { + // is this right, or should we just copy less than the full table? + return HB_Err_Invalid_Argument; + } + SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer); + return HB_Err_Ok; +} + diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp new file mode 100644 index 0000000..4fca143 --- /dev/null +++ b/src/ports/SkImageDecoder_CG.cpp @@ -0,0 +1,189 @@ +/* Copyright 2008, 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 <Carbon/Carbon.h> +#include "SkImageDecoder.h" +#include "SkImageEncoder.h" +#include "SkMovie.h" +#include "SkStream.h" +#include "SkTemplates.h" +#include "SkCGUtils.h" + +static void malloc_release_proc(void* info, const void* data, size_t size) { + sk_free(info); +} + +static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) { + // TODO: use callbacks, so we don't have to load all the data into RAM + size_t len = stream->getLength(); + void* data = sk_malloc_throw(len); + stream->read(data, len); + + return CGDataProviderCreateWithData(data, data, len, malloc_release_proc); +} + +static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) { + CGDataProviderRef data = SkStreamToDataProvider(stream); + CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0); + CGDataProviderRelease(data); + return imageSrc; +} + +class SkImageDecoder_CG : public SkImageDecoder { +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); +}; + +#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) + +bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { + CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); + + if (NULL == imageSrc) { + return false; + } + SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); + + CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL); + if (NULL == image) { + return false; + } + SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image); + + const int width = CGImageGetWidth(image); + const int height = CGImageGetHeight(image); + bm->setConfig(SkBitmap::kARGB_8888_Config, width, height); + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return true; + } + + if (!this->allocPixelRef(bm, NULL)) { + return false; + } + + bm->lockPixels(); + bm->eraseColor(0); + + CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, + 8, bm->rowBytes(), cs, BITMAP_INFO); + CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image); + CGContextRelease(cg); + CGColorSpaceRelease(cs); + + bm->unlockPixels(); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { + return SkNEW(SkImageDecoder_CG); +} + +///////////////////////////////////////////////////////////////////////// + +SkMovie* SkMovie::DecodeStream(SkStream* stream) { + return NULL; +} + +///////////////////////////////////////////////////////////////////////// + +static size_t consumer_put(void* info, const void* buffer, size_t count) { + SkWStream* stream = reinterpret_cast<SkWStream*>(info); + return stream->write(buffer, count) ? count : 0; +} + +static void consumer_release(void* info) { + // we do nothing, since by design we don't "own" the stream (i.e. info) +} + +static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) { + CGDataConsumerCallbacks procs; + procs.putBytes = consumer_put; + procs.releaseConsumer = consumer_release; + // we don't own/reference the stream, so it our consumer must not live + // longer that our caller's ownership of the stream + return CGDataConsumerCreate(stream, &procs); +} + +static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream, + CFStringRef type) { + CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream); + if (NULL == consumer) { + return NULL; + } + SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer); + + return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL); +} + +class SkImageEncoder_CG : public SkImageEncoder { +public: + SkImageEncoder_CG(Type t) : fType(t) {} + +protected: + virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); + +private: + Type fType; +}; + +/* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes + to our SkWStream. Since we don't reference/own the SkWStream, our consumer + must only live for the duration of the onEncode() method. + */ +bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm, + int quality) { + CFStringRef type; + switch (fType) { + case kJPEG_Type: + type = kUTTypeJPEG; + break; + case kPNG_Type: + type = kUTTypePNG; + break; + default: + return false; + } + + CGImageDestinationRef dst = SkStreamToImageDestination(stream, type); + if (NULL == dst) { + return false; + } + SkAutoTCallVProc<const void, CFRelease> ardst(dst); + + CGImageRef image = SkCreateCGImageRef(bm); + if (NULL == image) { + return false; + } + SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image); + + CGImageDestinationAddImage(dst, image, NULL); + CGImageDestinationFinalize(dst); + return true; +} + +SkImageEncoder* SkImageEncoder::Create(Type t) { + switch (t) { + case kJPEG_Type: + case kPNG_Type: + break; + default: + return NULL; + } + return SkNEW_ARGS(SkImageEncoder_CG, (t)); +} + diff --git a/src/ports/SkImageRef_ashmem.cpp b/src/ports/SkImageRef_ashmem.cpp index 5dd3a59..539d768 100644 --- a/src/ports/SkImageRef_ashmem.cpp +++ b/src/ports/SkImageRef_ashmem.cpp @@ -37,7 +37,7 @@ SkImageRef_ashmem::SkImageRef_ashmem(SkStream* stream, } SkImageRef_ashmem::~SkImageRef_ashmem() { - fCT->safeUnref(); + SkSafeUnref(fCT); this->closeFD(); } diff --git a/src/ports/SkMemory_brew.cpp b/src/ports/SkMemory_brew.cpp new file mode 100644 index 0000000..fe73d5b --- /dev/null +++ b/src/ports/SkMemory_brew.cpp @@ -0,0 +1,65 @@ +/* libs/graphics/ports/SkMemory_brew.cpp +** +** Copyright 2009, The Android Open Source Project +** Copyright 2009, Company 100, 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 "SkTypes.h" + +#ifdef SK_BUILD_FOR_BREW + +#include <AEEStdLib.h> + +void sk_throw() { + SkASSERT(!"sk_throw"); + abort(); +} + +void sk_out_of_memory(void) { + SkASSERT(!"sk_out_of_memory"); + abort(); +} + +void* sk_malloc_throw(size_t size) { + return sk_malloc_flags(size, SK_MALLOC_THROW); +} + +void* sk_realloc_throw(void* addr, size_t size) { + void* p = REALLOC(addr, size | ALLOC_NO_ZMEM); + if (size == 0) { + return p; + } + if (p == NULL) { + sk_throw(); + } + return p; +} + +void sk_free(void* p) { + FREEIF(p); +} + +void* sk_malloc_flags(size_t size, unsigned flags) { + void* p = MALLOC(size | ALLOC_NO_ZMEM); + if (p == NULL) { + if (flags & SK_MALLOC_THROW) { + sk_throw(); + } + } + return p; +} + +#endif + diff --git a/src/ports/SkMemory_malloc.cpp b/src/ports/SkMemory_malloc.cpp new file mode 100644 index 0000000..b10ef70 --- /dev/null +++ b/src/ports/SkMemory_malloc.cpp @@ -0,0 +1,45 @@ +#include "SkTypes.h" +#include <stdio.h> +#include <stdlib.h> + +void sk_throw() { + SkASSERT(!"sk_throw"); + abort(); +} + +void sk_out_of_memory(void) { + SkASSERT(!"sk_out_of_memory"); + abort(); +} + +void* sk_malloc_throw(size_t size) { + return sk_malloc_flags(size, SK_MALLOC_THROW); +} + +void* sk_realloc_throw(void* addr, size_t size) { + void* p = realloc(addr, size); + if (size == 0) { + return p; + } + if (p == NULL) { + sk_throw(); + } + return p; +} + +void sk_free(void* p) { + if (p) { + free(p); + } +} + +void* sk_malloc_flags(size_t size, unsigned flags) { + void* p = malloc(size); + if (p == NULL) { + if (flags & SK_MALLOC_THROW) { + sk_throw(); + } + } + return p; +} + diff --git a/src/ports/SkOSFile_brew.cpp b/src/ports/SkOSFile_brew.cpp new file mode 100644 index 0000000..9c7c072 --- /dev/null +++ b/src/ports/SkOSFile_brew.cpp @@ -0,0 +1,99 @@ +/* libs/graphics/ports/SkOSFile_brew.cpp +** +** Copyright 2006, The Android Open Source Project +** Copyright 2009, Company 100, 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 "SkOSFile.h" + +#ifdef SK_BUILD_FOR_BREW + +#include <AEEAppGen.h> +#include <AEEFile.h> +#include <AEEStdLib.h> + +SkFILE* sk_fopen(const char path[], SkFILE_Flags flags) +{ + int err; + OpenFileMode mode; + IFileMgr* fileMgr; + IFile* file; + IShell* shell; + + shell = reinterpret_cast<AEEApplet*>(GETAPPINSTANCE())->m_pIShell; + err = ISHELL_CreateInstance(shell, AEECLSID_FILEMGR, (void**)&fileMgr); + if (err!= SUCCESS) + return NULL; + + if (flags & kWrite_SkFILE_Flag) + mode = _OFM_READWRITE; + else /* kRead_SkFILE_Flag */ + mode = _OFM_READ; + + file = IFILEMGR_OpenFile(fileMgr, path, mode); + IFILEMGR_Release(fileMgr); + + return (SkFILE*)file; +} + +size_t sk_fgetsize(SkFILE* f) +{ + FileInfo fileInfo; + + IFILE_GetInfo((IFile*)f, &fileInfo); + return fileInfo.dwSize; +} + +bool sk_frewind(SkFILE* f) +{ + SkASSERT(f); + return IFILE_Seek((IFile*)f, _SEEK_START, 0) == SUCCESS; +} + +size_t sk_fread(void* buffer, size_t byteCount, SkFILE* f) +{ + SkASSERT(f); + if (buffer == NULL) + { + int err = IFILE_Seek((IFile*)f, _SEEK_CURRENT, (int)byteCount); + if (err == EFAILED) { + SkDEBUGF(("sk_fread: IFILE_Seek(%d) failed returned:%d\n", byteCount, err)); + return 0; + } + return byteCount; + } + else + return IFILE_Read((IFile*)f, buffer, byteCount); +} + +size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f) +{ + SkASSERT(f); + return IFILE_Write((IFile*)f, buffer, byteCount); +} + +void sk_fflush(SkFILE* f) +{ + SkASSERT(f); +} + +void sk_fclose(SkFILE* f) +{ + SkASSERT(f); + IFILE_Release((IFile*)f); +} + +#endif + diff --git a/src/ports/SkThread_win.cpp b/src/ports/SkThread_win.cpp index d3f3e21..cb3aa37 100644 --- a/src/ports/SkThread_win.cpp +++ b/src/ports/SkThread_win.cpp @@ -18,17 +18,6 @@ #include <windows.h> #include "SkThread.h" -namespace { - -template <bool> -struct CompileAssert { -}; - -} // namespace - -#define COMPILE_ASSERT(expr, msg) \ - typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] - int32_t sk_atomic_inc(int32_t* addr) { // InterlockedIncrement returns the new value, we want to return the old. @@ -42,8 +31,8 @@ int32_t sk_atomic_dec(int32_t* addr) SkMutex::SkMutex(bool /* isGlobal */) { - COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION), - NotEnoughSizeForCriticalSection); + SK_COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION), + NotEnoughSizeForCriticalSection); InitializeCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage)); } diff --git a/src/ports/SkTime_win.cpp b/src/ports/SkTime_win.cpp new file mode 100644 index 0000000..49bb37d --- /dev/null +++ b/src/ports/SkTime_win.cpp @@ -0,0 +1,46 @@ +/* libs/graphics/ports/SkTime_Unix.cpp +** +** Copyright 2009, 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 "SkTime.h" + +void SkTime::GetDateTime(DateTime* dt) +{ + if (dt) + { + SYSTEMTIME st; + GetSystemTime(&st); + + dt->fYear = st.wYear; + dt->fMonth = SkToU8(st.wMonth + 1); + dt->fDayOfWeek = SkToU8(st.wDayOfWeek); + dt->fDay = SkToU8(st.wDay); + dt->fHour = SkToU8(st.wHour); + dt->fMinute = SkToU8(st.wMinute); + dt->fSecond = SkToU8(st.wSecond); + } +} + +SkMSec SkTime::GetMSecs() +{ + FILETIME ft; + LARGE_INTEGER li; + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + __int64 t = li.QuadPart; /* In 100-nanosecond intervals */ + return (SkMSec)(t / 10000); /* In milliseconds */ +} diff --git a/src/svg/SkSVGParser.cpp b/src/svg/SkSVGParser.cpp index b55c5ed..df86178 100644 --- a/src/svg/SkSVGParser.cpp +++ b/src/svg/SkSVGParser.cpp @@ -43,7 +43,9 @@ static int gGeneratedMatrixID = 0; -SkSVGParser::SkSVGParser() : fHead(&fEmptyPaint), fIDs(256), +SkSVGParser::SkSVGParser(SkXMLParserError* errHandler) : + SkXMLParser(errHandler), + fHead(&fEmptyPaint), fIDs(256), fXMLWriter(&fStream), fCurrElement(NULL), fInSVG(false), fSuppressPaint(false) { fLastTransform.reset(); fEmptyPaint.f_fill.set("black"); @@ -157,7 +159,7 @@ bool SkSVGParser::onAddAttributeLen(const char name[], const char value[], size_ if (fCurrElement == NULL) // this signals we should ignore attributes for this element return true; if (fCurrElement->fIsDef == false && fCurrElement->fIsNotDef == false) - return true; // also an ignored element + return false; // also an ignored element size_t nameLen = strlen(name); int attrIndex = findAttribute(fCurrElement, name, nameLen, false); if (attrIndex == -1) { @@ -201,9 +203,11 @@ bool SkSVGParser::onStartElementLen(const char name[], size_t len) { if (nextColon && nextColon - name < len) return false; SkSVGTypes type = GetType(name, len); - SkASSERT(type >= 0); - if (type < 0) - return true; +// SkASSERT(type >= 0); + if (type < 0) { + type = SkSVGType_G; +// return true; + } SkSVGElement* parent = fParents.count() > 0 ? fParents.top() : NULL; SkSVGElement* element = CreateElement(type, parent); bool result = false; diff --git a/src/svg/SkSVGRect.cpp b/src/svg/SkSVGRect.cpp index 03fdfdc..8765d1d 100644 --- a/src/svg/SkSVGRect.cpp +++ b/src/svg/SkSVGRect.cpp @@ -33,7 +33,7 @@ SkSVGRect::SkSVGRect() { } void SkSVGRect::translate(SkSVGParser& parser, bool defState) { - parser._startElement("rectangle"); + parser._startElement("rect"); INHERITED::translate(parser, defState); SVG_ADD_ATTRIBUTE_ALIAS(left, x); SVG_ADD_ATTRIBUTE_ALIAS(top, y); diff --git a/src/svg/SkSVGSVG.cpp b/src/svg/SkSVGSVG.cpp index 1678fc1..9423c3a 100644 --- a/src/svg/SkSVGSVG.cpp +++ b/src/svg/SkSVGSVG.cpp @@ -27,9 +27,11 @@ const SkSVGAttribute SkSVGSVG::gAttributes[] = { SVG_ATTRIBUTE(width), SVG_ATTRIBUTE(version), SVG_ATTRIBUTE(viewBox), + SVG_ATTRIBUTE(x), SVG_LITERAL_ATTRIBUTE(xml:space, f_xml_space), SVG_ATTRIBUTE(xmlns), - SVG_LITERAL_ATTRIBUTE(xmlns:xlink, f_xml_xlink) + SVG_LITERAL_ATTRIBUTE(xmlns:xlink, f_xml_xlink), + SVG_ATTRIBUTE(y), }; DEFINE_SVG_INFO(SVG) diff --git a/src/svg/SkSVGSVG.h b/src/svg/SkSVGSVG.h index f331ccd..257c136 100644 --- a/src/svg/SkSVGSVG.h +++ b/src/svg/SkSVGSVG.h @@ -30,9 +30,12 @@ private: SkString f_width; SkString f_version; SkString f_viewBox; + SkString f_x; SkString f_xml_space; SkString f_xmlns; SkString f_xml_xlink; + SkString f_y; + typedef SkSVGElement INHERITED; }; diff --git a/src/text/SkTextLayout.cpp b/src/text/SkTextLayout.cpp new file mode 100644 index 0000000..09b3acd --- /dev/null +++ b/src/text/SkTextLayout.cpp @@ -0,0 +1,72 @@ +#include "SkTextLayout.h" + +SkTextStyle::SkTextStyle() { + fPaint.setAntiAlias(true); +} + +SkTextStyle::SkTextStyle(const SkTextStyle& src) : fPaint(src.fPaint) {} + +SkTextStyle::SkTextStyle(const SkPaint& paint) : fPaint(paint) {} + +SkTextStyle::~SkTextStyle() {} + +/////////////////////////////////////////////////////////////////////////////// + +SkTextLayout::SkTextLayout() { + fBounds.setEmpty(); + fDefaultStyle = new SkTextStyle; +} + +SkTextLayout::~SkTextLayout() { + fDefaultStyle->unref(); + fLines.deleteAll(); +} + +void SkTextLayout::setText(const char text[], size_t length) { + fText.setCount(length); + memcpy(fText.begin(), text, length); +} + +void SkTextLayout::setBounds(const SkRect& bounds) { + fBounds = bounds; + // if width changed, inval cache +} + +SkTextStyle* SkTextLayout::setDefaultStyle(SkTextStyle* style) { + SkRefCnt_SafeAssign(fDefaultStyle, style); + return style; +} + +/////////////////////////////////////////////////////////////////////////////// + +struct SkTextLayout::GlyphRun { + GlyphRun(); + ~GlyphRun(); + + SkPoint* fLocs; + uint16_t* fGlyphIDs; + int fCount; +}; + +SkTextLayout::GlyphRun::GlyphRun() : fLocs(NULL), fGlyphIDs(NULL), fCount(0) {} + +SkTextLayout::GlyphRun::~GlyphRun() { + delete[] fLocs; + delete[] fGlyphIDs; +} + +struct SkTextLayout::Line { + Line() {} + ~Line(); + + SkScalar fBaselineY; + SkTDArray<GlyphRun*> fRuns; +}; + +SkTextLayout::Line::~Line() { + fRuns.deleteAll(); +} + +void SkTextLayout::draw(SkCanvas* canvas) { +} + diff --git a/src/utils/SkCubicInterval.cpp b/src/utils/SkCubicInterval.cpp new file mode 100644 index 0000000..7a6084c --- /dev/null +++ b/src/utils/SkCubicInterval.cpp @@ -0,0 +1,61 @@ +#include "SkCubicInterval.h" + +static SkScalar eval_cubic(SkScalar c1, SkScalar c2, SkScalar c3, + SkScalar t) { + return SkScalarMul(SkScalarMul(SkScalarMul(c3, t) + c2, t) + c1, t); +} + +static SkScalar find_cubic_t(SkScalar c1, SkScalar c2, SkScalar c3, + SkScalar targetX) { + SkScalar minT = 0; + SkScalar maxT = SK_Scalar1; + SkScalar t; + + for (;;) { + t = SkScalarAve(minT, maxT); + SkScalar x = eval_cubic(c1, c2, c3, t); + if (SkScalarNearlyZero(x - targetX)) { + break; + } + // subdivide the range and try again + if (x < targetX) { + minT = t; + } else { + maxT = t; + } + } + return t; +} + +/* + a(1-t)^3 + 3bt(1-t)^2 + 3ct^2(1-t) + dt^3 + a: [0, 0] + d: [1, 1] + + 3bt - 6bt^2 + 3bt^3 + 3ct^2 - 3ct^3 + t^3 + C1 = t^1: 3b + C2 = t^2: 3c - 6b + C3 = t^3: 3b - 3c + 1 + + ((C3*t + C2)*t + C1)*t + */ +SkScalar SkEvalCubicInterval(SkScalar x1, SkScalar y1, + SkScalar x2, SkScalar y2, + SkScalar unitX) { + x1 = SkScalarPin(x1, 0, SK_Scalar1); + x2 = SkScalarPin(x2, 0, SK_Scalar1); + unitX = SkScalarPin(unitX, 0, SK_Scalar1); + + // First compute our coefficients in X + x1 *= 3; + x2 *= 3; + + // now search for t given unitX + SkScalar t = find_cubic_t(x1, x2 - 2*x1, x1 - x2 + SK_Scalar1, unitX); + + // now evaluate the cubic in Y + y1 *= 3; + y2 *= 3; + return eval_cubic(y1, y2 - 2*y1, y1 - y2 + SK_Scalar1, t); +} + diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp index 4ff7a50..223a4f7 100644 --- a/src/utils/SkDumpCanvas.cpp +++ b/src/utils/SkDumpCanvas.cpp @@ -1,5 +1,5 @@ #include "SkDumpCanvas.h" -#include "SkPicture.h" +#include "SkPicture.h" #include "SkPixelRef.h" #include "SkString.h" #include <stdarg.h> @@ -105,7 +105,7 @@ static const char* toString(SkBitmap::Config config) { static void toString(const SkBitmap& bm, SkString* str) { str->printf("bitmap:[%d %d] %s", bm.width(), bm.height(), toString(bm.config())); - + SkPixelRef* pr = bm.pixelRef(); if (NULL == pr) { // show null or the explicit pixel address (rare) @@ -140,7 +140,7 @@ static void toString(const void* text, size_t len, SkPaint::TextEncoding enc, /////////////////////////////////////////////////////////////////////////////// SkDumpCanvas::SkDumpCanvas(Dumper* dumper) : fNestLevel(0) { - dumper->safeRef(); + SkSafeRef(dumper); fDumper = dumper; static const int WIDE_OPEN = 16384; @@ -151,7 +151,7 @@ SkDumpCanvas::SkDumpCanvas(Dumper* dumper) : fNestLevel(0) { } SkDumpCanvas::~SkDumpCanvas() { - fDumper->safeUnref(); + SkSafeUnref(fDumper); } void SkDumpCanvas::dump(Verb verb, const SkPaint* paint, @@ -163,7 +163,7 @@ void SkDumpCanvas::dump(Verb verb, const SkPaint* paint, va_start(args, format); vsnprintf(buffer, BUFFER_SIZE, format, args); va_end(args); - + if (fDumper) { fDumper->dump(this, verb, buffer, paint); } @@ -420,7 +420,7 @@ void SkFormatDumper::dump(SkDumpCanvas* canvas, SkDumpCanvas::Verb verb, tab.append("\t"); } msg.printf("%s%s", tab.c_str(), str); - + if (p) { msg.appendf(" color:0x%08X flags:%X", p->getColor(), p->getFlags()); appendFlattenable(&msg, p->getShader(), "shader"); @@ -429,13 +429,13 @@ void SkFormatDumper::dump(SkDumpCanvas* canvas, SkDumpCanvas::Verb verb, appendFlattenable(&msg, p->getMaskFilter(), "maskFilter"); appendFlattenable(&msg, p->getPathEffect(), "pathEffect"); appendFlattenable(&msg, p->getColorFilter(), "filter"); - + if (SkDumpCanvas::kDrawText_Verb == verb) { msg.appendf(" textSize:%g", SkScalarToFloat(p->getTextSize())); appendPtr(&msg, p->getTypeface(), "typeface"); } } - + fProc(msg.c_str(), fRefcon); } diff --git a/src/utils/SkEGLContext_none.cpp b/src/utils/SkEGLContext_none.cpp new file mode 100644 index 0000000..1c55c95 --- /dev/null +++ b/src/utils/SkEGLContext_none.cpp @@ -0,0 +1,11 @@ +#include "SkEGLContext.h" + +SkEGLContext::SkEGLContext() : fContext(NULL) { +} + +SkEGLContext::~SkEGLContext() { +} + +bool SkEGLContext::init(int width, int height) { + return false; +} diff --git a/src/utils/SkLayer.cpp b/src/utils/SkLayer.cpp index 5c68261..1c484bd 100644 --- a/src/utils/SkLayer.cpp +++ b/src/utils/SkLayer.cpp @@ -27,7 +27,7 @@ SkLayer::SkLayer() { #endif } -SkLayer::SkLayer(const SkLayer& src) { +SkLayer::SkLayer(const SkLayer& src) : INHERITED() { fParent = NULL; m_opacity = src.m_opacity; m_size = src.m_size; diff --git a/src/utils/SkOSFile.cpp b/src/utils/SkOSFile.cpp index 47f43ba..982bc08 100644 --- a/src/utils/SkOSFile.cpp +++ b/src/utils/SkOSFile.cpp @@ -172,9 +172,8 @@ static bool issuffixfor(const SkString& suffix, const char str[]) { size_t suffixLen = suffix.size(); size_t strLen = strlen(str); - + return strLen >= suffixLen && - suffixLen == 0 || memcmp(suffix.c_str(), str + strLen - suffixLen, suffixLen) == 0; } diff --git a/src/xml/SkParseColor.cpp b/src/utils/SkParseColor.cpp index c451253..43d0737 100644 --- a/src/xml/SkParseColor.cpp +++ b/src/utils/SkParseColor.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -21,7 +21,7 @@ #include "SkString.h" // compress names 6 chars per long (packed 5 bits/char ) - // note: little advantage to splitting chars across longs, since 3 longs at 2 unused bits each + // note: little advantage to splitting chars across longs, since 3 longs at 2 unused bits each // allow for one additional split char (vs. the 18 unsplit chars in the three longs) // use extra two bits to represent: // 00 : final 6 (or fewer) chars (if 'a' is 0x01, zero could have special meaning) @@ -185,6 +185,7 @@ static const struct SkNameRGB { int colorNamesSize = sizeof(colorNames) / sizeof(colorNames[0]); +#ifdef SK_SUPPORT_UNITTEST static void CreateTable() { SkString comment; size_t originalSize = 0; @@ -226,6 +227,7 @@ static void CreateTable() { SkDebugf("// original = %d : replacement = %d\n", originalSize, replacement); SkASSERT(0); // always stop after creating table } +#endif #endif @@ -401,7 +403,7 @@ const char* SkParse::FindNamedColor(const char* name, size_t len, SkColor* color } ch = *namePtr | 0x20; last = ch < 'a' || ch > 'z'; - if (last) + if (last) sixMatch &= ~1; len -= 6; *sixMatchPtr++ = sixMatch; @@ -493,7 +495,7 @@ const char* SkParse::FindColor(const char* value, SkColor* colorPtr) { // if (end == NULL) // return NULL; // !!! range check for errors? -// *colorPtr = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]), +// *colorPtr = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]), // SkScalarRound(array[2]), SkScalarRound(array[3])); // return end; } else @@ -518,9 +520,9 @@ void SkParse::TestColor() { char bad[24]; size_t len = strlen(nameRGB.name); memcpy(bad, nameRGB.name, len); - bad[len - 1] -= 1; + bad[len - 1] -= 1; SkASSERT(FindColor(bad, &result) == false); - bad[len - 1] += 2; + bad[len - 1] += 2; SkASSERT(FindColor(bad, &result) == false); } result = SK_ColorBLACK; diff --git a/src/utils/SkParsePath.cpp b/src/utils/SkParsePath.cpp new file mode 100644 index 0000000..6b3afca --- /dev/null +++ b/src/utils/SkParsePath.cpp @@ -0,0 +1,239 @@ +#include "SkParse.h" +#include "SkParsePath.h" + +static inline bool is_between(int c, int min, int max) { + return (unsigned)(c - min) <= (unsigned)(max - min); +} + +static inline bool is_ws(int c) { + return is_between(c, 1, 32); +} + +static inline bool is_digit(int c) { + return is_between(c, '0', '9'); +} + +static inline bool is_sep(int c) { + return is_ws(c) || c == ','; +} + +static inline bool is_lower(int c) { + return is_between(c, 'a', 'z'); +} + +static inline int to_upper(int c) { + return c - 'a' + 'A'; +} + +static const char* skip_ws(const char str[]) { + SkASSERT(str); + while (is_ws(*str)) + str++; + return str; +} + +static const char* skip_sep(const char str[]) { + SkASSERT(str); + while (is_sep(*str)) + str++; + return str; +} + +static const char* find_points(const char str[], SkPoint value[], int count, + bool isRelative, SkPoint* relative) { + str = SkParse::FindScalars(str, &value[0].fX, count * 2); + if (isRelative) { + for (int index = 0; index < count; index++) { + value[index].fX += relative->fX; + value[index].fY += relative->fY; + } + } + return str; +} + +static const char* find_scalar(const char str[], SkScalar* value, + bool isRelative, SkScalar relative) { + str = SkParse::FindScalar(str, value); + if (isRelative) { + *value += relative; + } + return str; +} + +bool SkParsePath::FromSVGString(const char data[], SkPath* result) { + SkPath path; + SkPoint f = {0, 0}; + SkPoint c = {0, 0}; + SkPoint lastc = {0, 0}; + SkPoint points[3]; + char op = '\0'; + char previousOp = '\0'; + bool relative = false; + for (;;) { + data = skip_ws(data); + if (data[0] == '\0') { + break; + } + char ch = data[0]; + if (is_digit(ch) || ch == '-' || ch == '+') { + if (op == '\0') { + return false; + } + } else { + op = ch; + relative = false; + if (is_lower(op)) { + op = (char) to_upper(op); + relative = true; + } + data++; + data = skip_sep(data); + } + switch (op) { + case 'M': + data = find_points(data, points, 1, relative, &c); + path.moveTo(points[0]); + op = 'L'; + c = points[0]; + break; + case 'L': + data = find_points(data, points, 1, relative, &c); + path.lineTo(points[0]); + c = points[0]; + break; + case 'H': { + SkScalar x; + data = find_scalar(data, &x, relative, c.fX); + path.lineTo(x, c.fY); + c.fX = x; + } break; + case 'V': { + SkScalar y; + data = find_scalar(data, &y, relative, c.fY); + path.lineTo(c.fX, y); + c.fY = y; + } break; + case 'C': + data = find_points(data, points, 3, relative, &c); + goto cubicCommon; + case 'S': + data = find_points(data, &points[1], 2, relative, &c); + points[0] = c; + if (previousOp == 'C' || previousOp == 'S') { + points[0].fX -= lastc.fX - c.fX; + points[0].fY -= lastc.fY - c.fY; + } + cubicCommon: + path.cubicTo(points[0], points[1], points[2]); + lastc = points[1]; + c = points[2]; + break; + case 'Q': // Quadratic Bezier Curve + data = find_points(data, points, 2, relative, &c); + goto quadraticCommon; + case 'T': + data = find_points(data, &points[1], 1, relative, &c); + points[0] = points[1]; + if (previousOp == 'Q' || previousOp == 'T') { + points[0].fX = c.fX * 2 - lastc.fX; + points[0].fY = c.fY * 2 - lastc.fY; + } + quadraticCommon: + path.quadTo(points[0], points[1]); + lastc = points[0]; + c = points[1]; + break; + case 'Z': + path.close(); +#if 0 // !!! still a bug? + if (fPath.isEmpty() && (f.fX != 0 || f.fY != 0)) { + c.fX -= SkScalar.Epsilon; // !!! enough? + fPath.moveTo(c); + fPath.lineTo(f); + fPath.close(); + } +#endif + c = f; + op = '\0'; + break; + case '~': { + SkPoint args[2]; + data = find_points(data, args, 2, false, NULL); + path.moveTo(args[0].fX, args[0].fY); + path.lineTo(args[1].fX, args[1].fY); + } break; + default: + return false; + } + if (previousOp == 0) { + f = c; + } + previousOp = op; + } + // we're good, go ahead and swap in the result + result->swap(path); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkString.h" +#include "SkStream.h" + +static void write_scalar(SkWStream* stream, SkScalar value) { +#ifdef SK_SCALAR_IS_FLOAT + char buffer[64]; +#ifdef SK_BUILD_FOR_WIN32 + int len = sprintf(buffer, "%g", value); +#else + int len = snprintf(buffer, sizeof(buffer), "%g", value); +#endif + char* stop = buffer + len; +#else + char buffer[SkStrAppendScalar_MaxSize]; + char* stop = SkStrAppendScalar(buffer, value); +#endif + stream->write(buffer, stop - buffer); +} + +static void append_scalars(SkWStream* stream, char verb, const SkScalar data[], + int count) { + stream->write(&verb, 1); + write_scalar(stream, data[0]); + for (int i = 1; i < count; i++) { + stream->write(" ", 1); + write_scalar(stream, data[i]); + } +} + +void SkParsePath::ToSVGString(const SkPath& path, SkString* str) { + SkDynamicMemoryWStream stream; + + SkPath::Iter iter(path, false); + SkPoint pts[4]; + + for (;;) { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + append_scalars(&stream, 'M', &pts[0].fX, 2); + break; + case SkPath::kLine_Verb: + append_scalars(&stream, 'L', &pts[1].fX, 2); + break; + case SkPath::kQuad_Verb: + append_scalars(&stream, 'Q', &pts[1].fX, 4); + break; + case SkPath::kCubic_Verb: + append_scalars(&stream, 'C', &pts[1].fX, 6); + break; + case SkPath::kClose_Verb: + stream.write("Z", 1); + break; + case SkPath::kDone_Verb: + str->resize(stream.getOffset()); + stream.copyTo(str->writable_str()); + return; + } + } +} + diff --git a/src/utils/SkProxyCanvas.cpp b/src/utils/SkProxyCanvas.cpp index c643c82..4336181 100644 --- a/src/utils/SkProxyCanvas.cpp +++ b/src/utils/SkProxyCanvas.cpp @@ -1,13 +1,13 @@ #include "SkProxyCanvas.h" SkProxyCanvas::SkProxyCanvas(SkCanvas* proxy) : fProxy(proxy) { - fProxy->safeRef(); + SkSafeRef(fProxy); } SkProxyCanvas::~SkProxyCanvas() { - fProxy->safeUnref(); + SkSafeUnref(fProxy); } - + void SkProxyCanvas::setProxy(SkCanvas* proxy) { SkRefCnt_SafeAssign(fProxy, proxy); } @@ -18,14 +18,6 @@ bool SkProxyCanvas::getViewport(SkIPoint* size) const { return fProxy->getViewport(size); } -bool SkProxyCanvas::setViewport(int x, int y) { - return fProxy->setViewport(x, y); -} - -SkDevice* SkProxyCanvas::setBitmapDevice(const SkBitmap& bitmap) { - return fProxy->setBitmapDevice(bitmap); -} - int SkProxyCanvas::save(SaveFlags flags) { return fProxy->save(flags); } @@ -163,8 +155,8 @@ SkDrawFilter* SkProxyCanvas::setDrawFilter(SkDrawFilter* filter) { return fProxy->setDrawFilter(filter); } -SkDevice* SkProxyCanvas::createDevice(SkBitmap::Config config, int width, - int height, bool isOpaque, bool isForLayer) { +SkDevice* SkProxyCanvas::createDevice(SkBitmap::Config config, int width, int height, + bool isOpaque, bool isForLayer) { return fProxy->createDevice(config, width, height, isOpaque, isForLayer); } diff --git a/src/utils/SkSfntUtils.cpp b/src/utils/SkSfntUtils.cpp new file mode 100644 index 0000000..ba9f3f6 --- /dev/null +++ b/src/utils/SkSfntUtils.cpp @@ -0,0 +1,87 @@ +#include "SkEndian.h" +#include "SkSfntUtils.h" + +static uint16_t parse_be16(const uint8_t*& p) { + uint16_t value = (p[0] << 8) | p[1]; + p += 2; + return value; +} + +static uint32_t parse_be32(const uint8_t*& p) { + uint32_t value = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + p += 4; + return value; +} + +static Sk64 parse_be64(const uint8_t*& p) { + Sk64 value; + value.fHi = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + value.fLo = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; + p += 8; + return value; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkSfntUtils::ReadTable_head(SkFontID fontID, SkSfntTable_head* head) { + static const uint32_t gTag = SkSetFourByteTag('h', 'e', 'a', 'd'); + static const size_t gSize = 54; + + uint8_t storage[gSize]; + size_t size = SkFontHost::GetTableData(fontID, gTag, 0, gSize, storage); + if (size != gSize) { + return false; + } + + const uint8_t* p = storage; + head->fVersion = parse_be32(p); + head->fRevision = parse_be32(p); + head->fCheckSumAdjustment = parse_be32(p); + head->fMagicNumber = parse_be32(p); + head->fFlags = parse_be16(p); + head->fUnitsPerEm = parse_be16(p); + head->fDateCreated = parse_be64(p); + head->fDateModified = parse_be64(p); + head->fXMin = parse_be16(p); + head->fXMin = parse_be16(p); + head->fXMin = parse_be16(p); + head->fXMin = parse_be16(p); + head->fMacStyle = parse_be16(p); + head->fLowestPPEM = parse_be16(p); + head->fFontDirectionHint = parse_be16(p); + head->fIndexToLocFormat = parse_be16(p); + head->fGlyphDataFormat = parse_be16(p); + SkASSERT(p - storage == (long)size); + return true; +} + +bool SkSfntUtils::ReadTable_maxp(SkFontID fontID, SkSfntTable_maxp* maxp) { + static const uint32_t gTag = SkSetFourByteTag('m', 'a', 'x', 'p'); + static const size_t gSize = 32; + + uint8_t storage[gSize]; + size_t size = SkFontHost::GetTableData(fontID, gTag, 0, gSize, storage); + if (size != gSize) { + return false; + } + + const uint8_t* p = storage; + maxp->fVersion = parse_be32(p); + maxp->fNumGlyphs = parse_be16(p); + maxp->fMaxPoints = parse_be16(p); + maxp->fMaxContours = parse_be16(p); + maxp->fMaxComponentPoints = parse_be16(p); + maxp->fMaxComponentContours = parse_be16(p); + maxp->fMaxZones = parse_be16(p); + maxp->fMaxTwilightPoints = parse_be16(p); + maxp->fMaxStorage = parse_be16(p); + maxp->fMaxFunctionDefs = parse_be16(p); + maxp->fMaxInstructionDefs = parse_be16(p); + maxp->fMaxStackElements = parse_be16(p); + maxp->fMaxSizeOfInstructions = parse_be16(p); + maxp->fMaxComponentElements = parse_be16(p); + maxp->fMaxComponentDepth = parse_be16(p); + SkASSERT(p - storage == (long)size); + return true; +} + diff --git a/src/utils/mac/SkBitmap_Mac.cpp b/src/utils/mac/SkBitmap_Mac.cpp deleted file mode 100644 index 06c2b27..0000000 --- a/src/utils/mac/SkBitmap_Mac.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#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 deleted file mode 100644 index 5c96e21..0000000 --- a/src/utils/mac/SkCreateCGImageRef.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "SkCGUtils.h" -#include "SkBitmap.h" -#include "SkColorPriv.h" - -extern CGImageRef SkCreateCGImageRef(const SkBitmap&); - -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 = kCGBitmapByteOrder32Host | - 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 SkCreateCGImageRef(const SkBitmap& bm) { - size_t bitsPerComponent; - CGBitmapInfo info; - - 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); - - CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - CGImageRef ref = CGImageCreate(w, h, bitsPerComponent, - bitmap->bytesPerPixel() * 8, - bitmap->rowBytes(), space, info, dataRef, - NULL, false, kCGRenderingIntentDefault); - CGColorSpaceRelease(space); - CGDataProviderRelease(dataRef); - return ref; -} - - diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp index 67549b4..ec4a7b4 100644 --- a/src/views/SkEvent.cpp +++ b/src/views/SkEvent.cpp @@ -536,6 +536,21 @@ void SkEvent::ServiceQueueTimer() 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() diff --git a/src/views/SkMetaData.cpp b/src/views/SkMetaData.cpp index c366bd3..c871efb 100644 --- a/src/views/SkMetaData.cpp +++ b/src/views/SkMetaData.cpp @@ -89,6 +89,10 @@ void SkMetaData::setBool(const char name[], bool value) (void)this->set(name, &value, sizeof(bool), kBool_Type, 1); } +void SkMetaData::setData(const char name[], const void* data, size_t byteCount) { + (void)this->set(name, data, sizeof(char), kData_Type, byteCount); +} + void* SkMetaData::set(const char name[], const void* data, size_t dataSize, Type type, int count) { SkASSERT(name); @@ -129,6 +133,9 @@ void* SkMetaData::set(const char name[], const void* data, size_t dataSize, Type case kBool_Type: rec->fData.fBool = *(const bool*)rec->data(); break; + case kData_Type: + rec->fData.fPtr = rec->data(); + break; default: SkASSERT(!"bad type"); break; @@ -213,6 +220,18 @@ bool SkMetaData::findBool(const char name[], bool* value) const return false; } +const void* SkMetaData::findData(const char name[], size_t* length) const { + const Rec* rec = this->find(name, kData_Type); + if (rec) { + SkASSERT(rec->fDataLen == sizeof(char)); + if (length) { + *length = rec->fDataCount; + } + return rec->data(); + } + return NULL; +} + const SkMetaData::Rec* SkMetaData::find(const char name[], Type type) const { const Rec* rec = fRec; @@ -272,6 +291,10 @@ bool SkMetaData::removeBool(const char name[]) return this->remove(name, kBool_Type); } +bool SkMetaData::removeData(const char name[]) { + return this->remove(name, kData_Type); +} + /////////////////////////////////////////////////////////////////////////////////// SkMetaData::Iter::Iter(const SkMetaData& metadata) diff --git a/src/views/SkOSSound.cpp b/src/views/SkOSSound.cpp deleted file mode 100644 index 209a736..0000000 --- a/src/views/SkOSSound.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include "SkOSSound.h" - -#ifdef SK_BUILD_FOR_WIN - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -#include <Mmreg.h> -#if defined _WIN32 && _MSC_VER >= 1300 // disable nameless struct/union -#pragma warning ( push ) -#pragma warning ( disable : 4201 ) -#endif -#include <Mmsystem.h> -#if defined _WIN32 && _MSC_VER >= 1300 -#pragma warning ( pop ) -#endif -#include <stdio.h> - -class CWaveFile { -public: - BOOL Open(const char path[]); - void Close(); - - long Read(char* pData, long nLength); - - long GetLength() const {return m_nLength;} - WAVEFORMATEX* GetWaveFormat() {return (&m_Format);} - -protected: - FILE* m_pFile; - long m_nLength; - WAVEFORMATEX m_Format; - -private: - enum { - WF_OFFSET_FORMATTAG = 20, - WF_OFFSET_CHANNELS = 22, - WF_OFFSET_SAMPLESPERSEC = 24, - WF_OFFSET_AVGBYTESPERSEC = 28, - WF_OFFSET_BLOCKALIGN = 32, - WF_OFFSET_BITSPERSAMPLE = 34, - WF_OFFSET_DATASIZE = 40, - WF_OFFSET_DATA = 44, - WF_HEADER_SIZE = WF_OFFSET_DATA - }; -}; - -BOOL CWaveFile::Open(const char path[]) -{ - BYTE aHeader[WF_HEADER_SIZE]; - -/* hResInfo = FindResource (hInst, lpName, "WAVE"); - - if (hResInfo == NULL) - return FALSE; - - // Load the wave resource. - hRes = LoadResource (hInst, hResInfo); - - if (hRes == NULL) - return FALSE; - - // Lock the wave resource and play it. - lpRes = LockResource (0); -*/ - - - // open file -// m_pFile = _tfopen(szFileName, TEXT("rb")); - m_pFile = fopen(path, "rb"); - if (!m_pFile) { - return FALSE; - } - - // set file length - fseek(m_pFile, 0, SEEK_END); - m_nLength = ftell(m_pFile) - WF_HEADER_SIZE; - - // set the format attribute members - fseek(m_pFile, 0, SEEK_SET); - fread(aHeader, 1, WF_HEADER_SIZE, m_pFile); - m_Format.wFormatTag = *((WORD*) (aHeader + WF_OFFSET_FORMATTAG)); - m_Format.nChannels = *((WORD*) (aHeader + WF_OFFSET_CHANNELS)); - m_Format.nSamplesPerSec = *((DWORD*) (aHeader + WF_OFFSET_SAMPLESPERSEC)); - m_Format.nAvgBytesPerSec = *((DWORD*) (aHeader + WF_OFFSET_AVGBYTESPERSEC)); - m_Format.nBlockAlign = *((WORD*) (aHeader + WF_OFFSET_BLOCKALIGN)); - m_Format.wBitsPerSample = *((WORD*) (aHeader + WF_OFFSET_BITSPERSAMPLE)); - - return TRUE; -} - -void CWaveFile::Close() -{ - fclose(m_pFile); -} - -long CWaveFile::Read(char* pData, long nLength) -{ - return fread(pData, 1, nLength, m_pFile); -} - -//////////////////////////////////////////////////////////////////////////////////////// - -struct SkOSSoundWave { - HWAVEOUT hwo; - WAVEHDR whdr; - DWORD dwOldVolume; - CWaveFile waveFile; - HANDLE hDoneEvent; -}; - -static SkOSSoundWave gWave; -static bool gWavePaused; -static U8 gVolume; -static bool gInited = false; - -static void init_wave() -{ - if (gInited == false) - { - gWave.hwo = NULL; - gWavePaused = false; - gVolume = 0x80; - gInited = true; - } -} - -MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol); -MMRESULT EndWave(SkOSSoundWave* wave); - -#define MAX_ERRMSG 256 - -//#include "SkOSFile.h" // for utf16 - -void SkOSSound::Play(const char path[]) -{ - init_wave(); - - if (gWave.hwo != NULL) - SkOSSound::Stop(); - - U32 v32 = (gVolume << 8) | gVolume; // fill it out to 16bits - v32 |= v32 << 16; // set the left and right channels - - StartWave(path, &gWave, v32); - gWavePaused = false; -} - -bool SkOSSound::TogglePause() -{ - init_wave(); - - if (gWavePaused) - SkOSSound::Resume(); - else - SkOSSound::Pause(); - return !gWavePaused; -} - - -void SkOSSound::Pause() -{ - init_wave(); - - if (gWave.hwo == NULL || (gWave.whdr.dwFlags & WHDR_DONE)) - return; - waveOutPause(gWave.hwo); - gWavePaused = true; -} - -void SkOSSound::Resume() -{ - init_wave(); - - if (gWave.hwo == NULL || (gWave.whdr.dwFlags & WHDR_DONE)) - return; - waveOutRestart(gWave.hwo); - gWavePaused = false; -} - -void SkOSSound::Stop() -{ - init_wave(); - -// if (gWave.hwo == NULL || (gWave.whdr.dwFlags & WHDR_DONE)) - if (gWave.hwo == NULL) - return; - waveOutReset(gWave.hwo); - EndWave(&gWave); - gWavePaused = false; - gWave.hwo = NULL; -} - -U8 SkOSSound::GetVolume() -{ - init_wave(); - return gVolume; -} - -void SkOSSound::SetVolume(U8CPU vol) -{ - if ((int)vol < 0) - vol = 0; - else if (vol > 255) - vol = 255; - - init_wave(); - gVolume = SkToU8(vol); - - if (gWave.hwo) - { - unsigned long v32 = (vol << 8) | vol; // fill it out to 16bits - v32 |= v32 << 16; // set the left and right channels - waveOutSetVolume(gWave.hwo, v32); - } -} - -#if 0 -unsigned long SoundManager::GetPosition() -{ - if (fWave.hwo == NULL) - return 0; - MMTIME time; - time.wType = TIME_MS; - if (waveOutGetPosition(fWave.hwo, &time, sizeof(time)) == MMSYSERR_NOERROR && - time.wType == TIME_MS) - { - return time.u.ms; - } - return 0; -} -#endif - -MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol) -{ - HWAVEOUT hwo = NULL; -// WAVEHDR whdr; - MMRESULT mmres = 0; -// CWaveFile waveFile; -// HANDLE hDoneEvent = wave.hDoneEvent = -// CreateEvent(NULL, FALSE, FALSE, TEXT("DONE_EVENT")); - UINT devId; -// DWORD dwOldVolume; - - // Open wave file - if (!wave->waveFile.Open(path)) { -// TCHAR szErrMsg[MAX_ERRMSG]; -// _stprintf(szErrMsg, TEXT("Unable to open file: %s\n"), szWavFile); -// MessageBox(NULL, szErrMsg, TEXT("File I/O Error"), MB_OK); - return MMSYSERR_NOERROR; - } - - // Open audio device - for (devId = 0; devId < waveOutGetNumDevs(); devId++) - { - mmres = waveOutOpen(&hwo, devId, wave->waveFile.GetWaveFormat(), 0, 0, CALLBACK_NULL); - if (mmres == MMSYSERR_NOERROR) - { - wave->hwo = hwo; - break; - } - } - if (mmres != MMSYSERR_NOERROR) - { - SkDEBUGCODE(SkDebugf("waveOutOpen(%s) -> %d\n", path, mmres);) - return mmres; - } - - // Set volume - mmres = waveOutGetVolume(hwo, &wave->dwOldVolume); - if (mmres != MMSYSERR_NOERROR) { - return mmres; - } - - waveOutSetVolume(hwo, vol); - if (mmres != MMSYSERR_NOERROR) { - return mmres; - } - - // Initialize wave header - ZeroMemory(&wave->whdr, sizeof(WAVEHDR)); - wave->whdr.lpData = new char[wave->waveFile.GetLength()]; - wave->whdr.dwBufferLength = wave->waveFile.GetLength(); - wave->whdr.dwUser = 0; - wave->whdr.dwFlags = 0; - wave->whdr.dwLoops = 0; - wave->whdr.dwBytesRecorded = 0; - wave->whdr.lpNext = 0; - wave->whdr.reserved = 0; - - // Play buffer - wave->waveFile.Read(wave->whdr.lpData, wave->whdr.dwBufferLength); - - mmres = waveOutPrepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR)); - if (mmres != MMSYSERR_NOERROR) { - return mmres; - } - - mmres = waveOutWrite(hwo, &wave->whdr, sizeof(WAVEHDR)); -// if (mmres != MMSYSERR_NOERROR) { - return mmres; -// } -} - -#if 0 -void IdleWave(Wave& wave) -{ - // Wait for audio to finish playing - while (!(wave.whdr.dwFlags & WHDR_DONE)) { - WaitForSingleObject(wave.hDoneEvent, INFINITE); - } -} -#endif - -MMRESULT EndWave(SkOSSoundWave* wave) -{ - HWAVEOUT hwo = wave->hwo; - MMRESULT mmres; - // Clean up - mmres = waveOutUnprepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR)); - if (mmres != MMSYSERR_NOERROR) { - return mmres; - } - - waveOutSetVolume(hwo, wave->dwOldVolume); - if (mmres != MMSYSERR_NOERROR) { - return mmres; - } - - mmres = waveOutClose(hwo); - if (mmres != MMSYSERR_NOERROR) { - return mmres; - } - - delete [] wave->whdr.lpData; - wave->waveFile.Close(); - - return MMSYSERR_NOERROR; -} - -#endif /* SK_BUILD_FOR_WIN */ - diff --git a/src/views/SkStaticTextView.cpp b/src/views/SkStaticTextView.cpp new file mode 100644 index 0000000..8fb8bc1 --- /dev/null +++ b/src/views/SkStaticTextView.cpp @@ -0,0 +1,177 @@ +#include "SkWidgetViews.h" +#include "SkTextBox.h" + +#ifdef SK_DEBUG +static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[]) +{ + const char* value = dom.findAttr(node, attr); + if (value) + SkDebugf("unknown attribute %s=\"%s\"\n", attr, value); +} +#else + #define assert_no_attr(dom, node, attr) +#endif + +SkStaticTextView::SkStaticTextView() +{ + fMargin.set(0, 0); + fMode = kFixedSize_Mode; + fSpacingAlign = SkTextBox::kStart_SpacingAlign; + +// init_skin_paint(kStaticText_SkinEnum, &fPaint); +} + +SkStaticTextView::~SkStaticTextView() +{ +} + +void SkStaticTextView::computeSize() +{ + if (fMode == kAutoWidth_Mode) + { + SkScalar width = fPaint.measureText(fText.c_str(), fText.size()); + this->setWidth(width + fMargin.fX * 2); + } + else if (fMode == kAutoHeight_Mode) + { + SkScalar width = this->width() - fMargin.fX * 2; + int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0; + + this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2); + } +} + +void SkStaticTextView::setMode(Mode mode) +{ + SkASSERT((unsigned)mode < kModeCount); + + if (fMode != mode) + { + fMode = SkToU8(mode); + this->computeSize(); + } +} + +void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align) +{ + fSpacingAlign = SkToU8(align); + this->inval(NULL); +} + +void SkStaticTextView::getMargin(SkPoint* margin) const +{ + if (margin) + *margin = fMargin; +} + +void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy) +{ + if (fMargin.fX != dx || fMargin.fY != dy) + { + fMargin.set(dx, dy); + this->computeSize(); + this->inval(NULL); + } +} + +size_t SkStaticTextView::getText(SkString* text) const +{ + if (text) + *text = fText; + return fText.size(); +} + +size_t SkStaticTextView::getText(char text[]) const +{ + if (text) + memcpy(text, fText.c_str(), fText.size()); + return fText.size(); +} + +void SkStaticTextView::setText(const SkString& text) +{ + this->setText(text.c_str(), text.size()); +} + +void SkStaticTextView::setText(const char text[]) +{ + if (text == NULL) + text = ""; + this->setText(text, strlen(text)); +} + +void SkStaticTextView::setText(const char text[], size_t len) +{ + if (!fText.equals(text, len)) + { + fText.set(text, len); + this->computeSize(); + this->inval(NULL); + } +} + +void SkStaticTextView::getPaint(SkPaint* paint) const +{ + if (paint) + *paint = fPaint; +} + +void SkStaticTextView::setPaint(const SkPaint& paint) +{ + if (fPaint != paint) + { + fPaint = paint; + this->computeSize(); + this->inval(NULL); + } +} + +void SkStaticTextView::onDraw(SkCanvas* canvas) +{ + this->INHERITED::onDraw(canvas); + + if (fText.isEmpty()) + return; + + SkTextBox box; + + box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode); + box.setSpacingAlign(this->getSpacingAlign()); + box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY); + box.draw(canvas, fText.c_str(), fText.size(), fPaint); +} + +void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node) +{ +#if 0 + this->INHERITED::onInflate(dom, node); + + int index; + if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0) + this->setMode((Mode)index); + else + assert_no_attr(dom, node, "mode"); + + if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0) + this->setSpacingAlign((SkTextBox::SpacingAlign)index); + else + assert_no_attr(dom, node, "spacing-align"); + + SkScalar s[2]; + if (dom.findScalars(node, "margin", s, 2)) + this->setMargin(s[0], s[1]); + else + assert_no_attr(dom, node, "margin"); + + const char* text = dom.findAttr(node, "text"); + if (text) + this->setText(text); + + if ((node = dom.getFirstChild(node, "paint")) != NULL && + (node = dom.getFirstChild(node, "screenplay")) != NULL) + { + inflate_paint(dom, node, &fPaint); + } +#endif +} + diff --git a/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp index 8bc82f3..0e31ac6 100644 --- a/src/views/SkTextBox.cpp +++ b/src/views/SkTextBox.cpp @@ -16,7 +16,7 @@ */ #include "SkTextBox.h" -#include "../src/core/SkGlyphCache.h" +#include "../core/SkGlyphCache.h" #include "SkUtils.h" #include "SkAutoKern.h" @@ -214,3 +214,24 @@ void SkTextBox::draw(SkCanvas* canvas, const char text[], size_t len, const SkPa } } +/////////////////////////////////////////////////////////////////////////////// + +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/SkView.cpp b/src/views/SkView.cpp index 652eb85..7969d3d 100644 --- a/src/views/SkView.cpp +++ b/src/views/SkView.cpp @@ -49,6 +49,10 @@ 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); @@ -87,12 +91,16 @@ void SkView::draw(SkCanvas* canvas) { SkRect r; r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight); - if (canvas->quickReject(r, SkCanvas::kBW_EdgeType)) - return; + if (this->isClipToBounds() && + canvas->quickReject(r, SkCanvas::kBW_EdgeType)) { + return; + } SkAutoCanvasRestore as(canvas, true); - canvas->clipRect(r); + if (this->isClipToBounds()) { + canvas->clipRect(r); + } canvas->translate(fLoc.fX, fLoc.fY); if (fParent) { @@ -119,37 +127,36 @@ void SkView::draw(SkCanvas* canvas) } } -void SkView::inval(SkRect* rect) -{ - if (!this->isVisible()) - return; - - SkRect bounds; - - this->getLocalBounds(&bounds); - if (rect && !bounds.intersect(*rect)) - return; - - rect = &bounds; +void SkView::inval(SkRect* rect) { SkView* view = this; + SkRect storage; - for (;;) - { - if (view->handleInval(bounds)) - break; + 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; + } - SkRect parentR; SkView* parent = view->fParent; + if (parent == NULL) { + return; + } - if (parent == NULL || !parent->isVisible()) - break; - - bounds.offset(view->fLoc.fX, view->fLoc.fY); - parent->getLocalBounds(&parentR); - if (!bounds.intersect(parentR)) - return; - - view = parent; + if (rect) { + rect->offset(view->fLoc.fX, view->fLoc.fY); + } + view = parent; } } @@ -456,7 +463,7 @@ bool SkView::onClick(Click*) { return false; } -bool SkView::handleInval(const SkRect& r) { +bool SkView::handleInval(const SkRect*) { return false; } diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp index b63b81a..dbc1eea 100644 --- a/src/views/SkWindow.cpp +++ b/src/views/SkWindow.cpp @@ -1,5 +1,6 @@ #include "SkWindow.h" #include "SkCanvas.h" +#include "SkDevice.h" #include "SkOSMenu.h" #include "SkSystemEventTypes.h" #include "SkTime.h" @@ -48,6 +49,8 @@ SkWindow::SkWindow() : fFocusView(NULL) #else fConfig = SkBitmap::kARGB_8888_Config; #endif + + fMatrix.reset(); } SkWindow::~SkWindow() @@ -57,6 +60,25 @@ SkWindow::~SkWindow() 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); @@ -72,6 +94,7 @@ void SkWindow::resize(int width, int height, SkBitmap::Config config) fConfig = config; fBitmap.setConfig(config, width, height); fBitmap.allocPixels(); + fBitmap.setIsOpaque(true); this->setSize(SkIntToScalar(width), SkIntToScalar(height)); this->inval(NULL); @@ -88,25 +111,31 @@ void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b) fBitmap.eraseRGB(r, g, b); } -bool SkWindow::handleInval(const SkRect& r) +bool SkWindow::handleInval(const SkRect* localR) { SkIRect ir; - r.round(&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, this->width(), this->height()); + } fDirtyRgn.op(ir, SkRegion::kUnion_Op); -#ifdef SK_BUILD_FOR_WIN32xxxx - if (!fWaitingOnInval) - { - fWaitingOnInval = true; - (new SkEvent(SK_EventDelayInval))->post(this->getSinkID(), 10); - } -#else this->onHandleInval(ir); -#endif return true; } +void SkWindow::forceInvalAll() { + fDirtyRgn.setRect(0, 0, this->width(), this->height()); +} + #if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN) #include <windows.h> #include <gx.h> @@ -117,7 +146,7 @@ bool SkWindow::handleInval(const SkRect& r) extern bool gEnableControlledThrow; #endif -bool SkWindow::update(SkIRect* updateArea) +bool SkWindow::update(SkIRect* updateArea, SkCanvas* canvas) { if (!fDirtyRgn.isEmpty()) { @@ -134,37 +163,49 @@ bool SkWindow::update(SkIRect* updateArea) bm.setPixels(buffer); #endif - SkCanvas canvas(bm); + SkCanvas rasterCanvas; + SkDevice* device; - canvas.clipRegion(fDirtyRgn); + 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); + canvas->setBounder(&b); #endif #ifdef SK_SIMULATE_FAILED_MALLOC gEnableControlledThrow = true; #endif #ifdef SK_BUILD_FOR_WIN32 - try { - this->draw(&canvas); - } - catch (...) { - } + //try { + this->draw(canvas); + //} + //catch (...) { + //} #else - this->draw(&canvas); + this->draw(canvas); #endif #ifdef SK_SIMULATE_FAILED_MALLOC gEnableControlledThrow = false; #endif #ifdef TEST_BOUNDER - canvas.setBounder(NULL); + canvas->setBounder(NULL); #endif #if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN) diff --git a/tests/Test.cpp b/tests/Test.cpp index 4bbe83a..2bcd3e0 100644 --- a/tests/Test.cpp +++ b/tests/Test.cpp @@ -42,7 +42,7 @@ void Reporter::endTest(Test* test) { Test::Test() : fReporter(NULL) {} Test::~Test() { - fReporter->safeUnref(); + SkSafeUnref(fReporter); } void Test::setReporter(Reporter* r) { |