diff options
author | reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2009-09-15 14:10:42 +0000 |
---|---|---|
committer | reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2009-09-15 14:10:42 +0000 |
commit | ed881c2704bc81fe46a68c0cf9e292287313baa6 (patch) | |
tree | b1cc4beed571a83994f7f8100c0cba76183f850c | |
parent | ebdeeb8a018f2df01e190fd961d68a94f0e0fcb9 (diff) |
add neon opts for matrix procs
git-svn-id: http://skia.googlecode.com/svn/trunk@353 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | include/core/SkPaint.h | 11 | ||||
-rw-r--r-- | samplecode/SampleStrokePath.cpp | 125 | ||||
-rw-r--r-- | src/core/SkBitmapProcState_matrixProcs.cpp | 105 | ||||
-rw-r--r-- | src/core/SkBitmapProcState_matrix_clamp.h | 826 | ||||
-rw-r--r-- | src/core/SkBitmapProcState_matrix_repeat.h | 580 | ||||
-rw-r--r-- | xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj | 8 |
6 files changed, 1647 insertions, 8 deletions
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index ed60a20f61..659d8753e7 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -256,11 +256,16 @@ public: /** Styles apply to rect, oval, path, and text. Bitmaps are always drawn in "fill", and lines are always drawn in "stroke". + + Note: strokeandfill implicitly draws the result with + SkPath::kWinding_FillType, so if the original path is even-odd, the + results may not appear the same as if it was drawn twice, filled and + then stroked. */ enum Style { - kFill_Style, //!< fill with the paint's color - kStroke_Style, //!< stroke with the paint's color - kStrokeAndFill_Style, //!< fill and stroke with the paint's color + kFill_Style, //!< fill the geometry + kStroke_Style, //!< stroke the geometry + kStrokeAndFill_Style, //!< fill and stroke the geometry kStyleCount, }; diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp new file mode 100644 index 0000000000..ec0a970e94 --- /dev/null +++ b/samplecode/SampleStrokePath.cpp @@ -0,0 +1,125 @@ +#include "SampleCode.h" +#include "SkCanvas.h" +#include "SkParsePath.h" +#include "SkPath.h" +#include "SkRandom.h" +#include "SkView.h" + +static void scale_to_width(SkPath* path, SkScalar dstWidth) { + const SkRect& bounds = path->getBounds(); + SkScalar scale = dstWidth / bounds.width(); + SkMatrix matrix; + + matrix.setScale(scale, scale); + path->transform(matrix); +} + +static const struct { + SkPaint::Style fStyle; + SkPaint::Join fJoin; + int fStrokeWidth; +} gRec[] = { + { SkPaint::kFill_Style, SkPaint::kMiter_Join, 0 }, + { SkPaint::kStroke_Style, SkPaint::kMiter_Join, 0 }, + { SkPaint::kStroke_Style, SkPaint::kMiter_Join, 10 }, + { SkPaint::kStrokeAndFill_Style, SkPaint::kMiter_Join, 10 }, +}; + +class StrokePathView : public SkView { + SkScalar fWidth; + SkPath fPath; +public: + StrokePathView() { + fWidth = SkIntToScalar(120); + +#if 0 + const char str[] = + "M 0, 3" + "C 10, -10, 30, -10, 0, 28" + "C -30, -10, -10, -10, 0, 3" + "Z"; + SkParsePath::FromSVGString(str, &fPath); +#else + fPath.addCircle(0, 0, SkIntToScalar(50), SkPath::kCW_Direction); + fPath.addCircle(0, SkIntToScalar(-50), SkIntToScalar(30), SkPath::kCW_Direction); +#endif + + scale_to_width(&fPath, fWidth); + const SkRect& bounds = fPath.getBounds(); + fPath.offset(-bounds.fLeft, -bounds.fTop); + } + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "StrokePath"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(0xFFDDDDDD); + } + + SkRandom rand; + + void drawSet(SkCanvas* canvas, SkPaint* paint) { + SkAutoCanvasRestore acr(canvas, true); + + for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { + paint->setStyle(gRec[i].fStyle); + paint->setStrokeJoin(gRec[i].fJoin); + paint->setStrokeWidth(SkIntToScalar(gRec[i].fStrokeWidth)); + canvas->drawPath(fPath, *paint); + canvas->translate(fWidth * 5 / 4, 0); + } + } + + virtual void onDraw(SkCanvas* canvas) { + drawBG(canvas); + canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SK_ColorBLUE); + +#if 1 + SkPath p; + float r = rand.nextUScalar1() + 0.5f; + SkScalar x = 0, y = 0; + p.moveTo(x, y); +#if 0 + p.cubicTo(x-75*r, y+75*r, x-40*r, y+125*r, x, y+85*r); + p.cubicTo(x+40*r, y+125*r, x+75*r, y+75*r, x, y); +#else + p.cubicTo(x+75*r, y+75*r, x+40*r, y+125*r, x, y+85*r); + p.cubicTo(x-40*r, y+125*r, x-75*r, y+75*r, x, y); +#endif + p.close(); + fPath = p; + fPath.offset(100, 0); +#endif + + fPath.setFillType(SkPath::kWinding_FillType); + drawSet(canvas, &paint); + + canvas->translate(0, fPath.getBounds().height() * 5 / 4); + fPath.setFillType(SkPath::kEvenOdd_FillType); + drawSet(canvas, &paint); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + this->inval(NULL); + return this->INHERITED::onFindClickHandler(x, y); + } +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new StrokePathView; } +static SkViewRegister reg(MyFactory); + diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp index 67b57c4418..6654312a8f 100644 --- a/src/core/SkBitmapProcState_matrixProcs.cpp +++ b/src/core/SkBitmapProcState_matrixProcs.cpp @@ -1,3 +1,5 @@ +/* NEON optimized code (C) COPYRIGHT 2009 Motorola */ + #include "SkBitmapProcState.h" #include "SkPerspIter.h" #include "SkShader.h" @@ -31,14 +33,22 @@ void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count); #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF) #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF) #define CHECK_FOR_DECAL -#include "SkBitmapProcState_matrix.h" +#if defined(__ARM_HAVE_NEON) + #include "SkBitmapProcState_matrix_clamp.h" +#else + #include "SkBitmapProcState_matrix.h" +#endif #define MAKENAME(suffix) RepeatX_RepeatY ## suffix #define TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) #define TILEY_PROCF(fy, max) (((fy) & 0xFFFF) * ((max) + 1) >> 16) #define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) #define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) -#include "SkBitmapProcState_matrix.h" +#if defined(__ARM_HAVE_NEON) + #include "SkBitmapProcState_matrix_repeat.h" +#else + #include "SkBitmapProcState_matrix.h" +#endif #define MAKENAME(suffix) GeneralXY ## suffix #define PREAMBLE(state) SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; \ @@ -147,6 +157,52 @@ void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) { int i; +#if defined(__ARM_HAVE_NEON) + if (count >= 8) { + /* SkFixed is 16.16 fixed point */ + SkFixed dx2 = dx+dx; + SkFixed dx4 = dx2+dx2; + SkFixed dx8 = dx4+dx4; + + /* now build fx/fx+dx/fx+2dx/fx+3dx */ + SkFixed fx1, fx2, fx3; + int32x2_t lower, upper; + int32x4_t lbase, hbase; + uint16_t *dst16 = (uint16_t *)dst; + + fx1 = fx+dx; + fx2 = fx1+dx; + fx3 = fx2+dx; + + /* avoid an 'lbase unitialized' warning */ + lbase = vdupq_n_s32(fx); + lbase = vsetq_lane_s32(fx1, lbase, 1); + lbase = vsetq_lane_s32(fx2, lbase, 2); + lbase = vsetq_lane_s32(fx3, lbase, 3); + hbase = vaddq_s32(lbase, vdupq_n_s32(dx4)); + + /* take upper 16 of each, store, and bump everything */ + do { + int32x4_t lout, hout; + uint16x8_t hi16; + + lout = lbase; + hout = hbase; + /* gets hi's of all louts then hi's of all houts */ + asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout)); + hi16 = vreinterpretq_u16_s32(hout); + vst1q_u16(dst16, hi16); + + /* on to the next */ + lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8)); + hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8)); + dst16 += 8; + count -= 8; + fx += dx8; + } while (count >= 8); + dst = (uint32_t *) dst16; + } +#else for (i = (count >> 2); i > 0; --i) { *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16); @@ -154,16 +210,55 @@ void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16); fx += dx+dx; } - uint16_t* xx = (uint16_t*)dst; + count &= 3; +#endif - for (i = (count & 3); i > 0; --i) - { + uint16_t* xx = (uint16_t*)dst; + for (i = count; i > 0; --i) { *xx++ = SkToU16(fx >> 16); fx += dx; } } void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) { + +#if defined(__ARM_HAVE_NEON) + if (count >= 8) { + int32x4_t wide_fx; + int32x4_t wide_fx2; + int32x4_t wide_dx8 = vdupq_n_s32(dx*8); + + wide_fx = vdupq_n_s32(fx); + wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1); + wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2); + wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3); + + wide_fx2 = vaddq_s32(wide_fx, vdupq_n_s32(dx+dx+dx+dx)); + + while (count >= 8) { + int32x4_t wide_out; + int32x4_t wide_out2; + + wide_out = vshlq_n_s32(vshrq_n_s32(wide_fx, 12), 14); + wide_out = vorrq_s32(wide_out, + vaddq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(1))); + + wide_out2 = vshlq_n_s32(vshrq_n_s32(wide_fx2, 12), 14); + wide_out2 = vorrq_s32(wide_out2, + vaddq_s32(vshrq_n_s32(wide_fx2,16), vdupq_n_s32(1))); + + vst1q_u32(dst, vreinterpretq_u32_s32(wide_out)); + vst1q_u32(dst+4, vreinterpretq_u32_s32(wide_out2)); + + dst += 8; + fx += dx*8; + wide_fx = vaddq_s32(wide_fx, wide_dx8); + wide_fx2 = vaddq_s32(wide_fx2, wide_dx8); + count -= 8; + } + } +#endif + if (count & 1) { SkASSERT((fx >> (16 + 14)) == 0); diff --git a/src/core/SkBitmapProcState_matrix_clamp.h b/src/core/SkBitmapProcState_matrix_clamp.h new file mode 100644 index 0000000000..c95a4fa065 --- /dev/null +++ b/src/core/SkBitmapProcState_matrix_clamp.h @@ -0,0 +1,826 @@ +/* NEON optimized code (C) COPYRIGHT 2009 Motorola */ +/* + * Modifications done in-house at Motorola + * + * this is a clone of SkBitmapProcState_matrix.h + * and has been tuned to work with the NEON unit. + * + * Still going back and forth between whether this approach + * (clone the entire SkBitmapProcState_matrix.h file or + * if I should put just the modified routines in here and + * then use a construct like #define DONT_DO_THIS_FUNCTION or + * something like that... + * + * This is for the ClampX_ClampY instance + * + */ + + +#if !defined(__ARM_HAVE_NEON) +#error this file can be used only when the NEON unit is enabled +#endif + +#include <arm_neon.h> + +/* + * This has been modified on the knowledge that (at the time) + * we had the following macro definitions in the parent file + * + * #define MAKENAME(suffix) ClampX_ClampY ## suffix + * #define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max) + * #define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max) + * #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF) + * #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF) + * #define CHECK_FOR_DECAL + */ + +/* SkClampMax(val,max) -- bound to 0..max */ + +#define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale) +#define SCALE_FILTER_NAME MAKENAME(_filter_scale) +#define AFFINE_NOFILTER_NAME MAKENAME(_nofilter_affine) +#define AFFINE_FILTER_NAME MAKENAME(_filter_affine) +#define PERSP_NOFILTER_NAME MAKENAME(_nofilter_persp) +#define PERSP_FILTER_NAME MAKENAME(_filter_persp) + +#define PACK_FILTER_X_NAME MAKENAME(_pack_filter_x) +#define PACK_FILTER_Y_NAME MAKENAME(_pack_filter_y) + +#ifndef PREAMBLE + #define PREAMBLE(state) + #define PREAMBLE_PARAM_X + #define PREAMBLE_PARAM_Y + #define PREAMBLE_ARG_X + #define PREAMBLE_ARG_Y +#endif + +static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask)) == 0); + + PREAMBLE(s); + // we store y, x, x, x, x, x + + const unsigned maxX = s.fBitmap->width() - 1; + SkFixed fx; + { + SkPoint pt; + s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &pt); + fx = SkScalarToFixed(pt.fY); + const unsigned maxY = s.fBitmap->height() - 1; + *xy++ = TILEY_PROCF(fx, maxY); + fx = SkScalarToFixed(pt.fX); + } + + if (0 == maxX) { + // all of the following X values must be 0 + memset(xy, 0, count * sizeof(uint16_t)); + return; + } + + const SkFixed dx = s.fInvSx; + +#ifdef CHECK_FOR_DECAL + // test if we don't need to apply the tile proc + if ((unsigned)(fx >> 16) <= maxX && + (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) { + decal_nofilter_scale(xy, fx, dx, count); + return; + } +#endif + + int i; + + /* very much like done in decal_nofilter, but with + * an extra clamping function applied. + * TILEX_PROCF(fx,max) SkClampMax((fx)>>16, max) + */ + if (count >= 8) { + /* SkFixed is 16.16 fixed point */ + SkFixed dx2 = dx+dx; + SkFixed dx4 = dx2+dx2; + SkFixed dx8 = dx4+dx4; + + /* now build fx/fx+dx/fx+2dx/fx+3dx */ + SkFixed fx1, fx2, fx3; + int32x2_t lower, upper; + int32x4_t lbase, hbase; + int16_t *dst16 = (int16_t *)xy; + + fx1 = fx+dx; + fx2 = fx1+dx; + fx3 = fx2+dx; + + /* build my template(s) */ + /* avoid the 'lbase unitialized' warning */ + lbase = vdupq_n_s32(fx); + lbase = vsetq_lane_s32(fx1, lbase, 1); + lbase = vsetq_lane_s32(fx2, lbase, 2); + lbase = vsetq_lane_s32(fx3, lbase, 3); + + hbase = vaddq_s32(lbase, vdupq_n_s32(dx4)); + + /* store & bump */ + do { + int32x4_t lout; + int32x4_t hout; + int16x8_t hi16; + + /* get the hi 16s of all those 32s */ + lout = lbase; + hout = hbase; + /* this sets up all lout's then all hout's in hout */ + asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout)); + hi16 = vreinterpretq_s16_s32(hout); + + /* clamp & output */ + hi16 = vmaxq_s16(hi16, vdupq_n_s16(0)); + hi16 = vminq_s16(hi16, vdupq_n_s16(maxX)); + vst1q_s16(dst16, hi16); + + /* but preserving base & on to the next */ + lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8)); + hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8)); + dst16 += 8; + count -= 8; + fx += dx8; + } while (count >= 8); + xy = (uint32_t *) dst16; + } + + uint16_t* xx = (uint16_t*)xy; + for (i = count; i > 0; --i) { + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + } +} + +// note: we could special-case on a matrix which is skewed in X but not Y. +// this would require a more general setup thatn SCALE does, but could use +// SCALE's inner loop that only looks at dx + +static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kAffine_Mask); + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask | + SkMatrix::kAffine_Mask)) == 0); + + PREAMBLE(s); + SkPoint srcPt; + s.fInvProc(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + SkFixed fx = SkScalarToFixed(srcPt.fX); + SkFixed fy = SkScalarToFixed(srcPt.fY); + SkFixed dx = s.fInvSx; + SkFixed dy = s.fInvKy; + int maxX = s.fBitmap->width() - 1; + int maxY = s.fBitmap->height() - 1; + + /* NEON lets us do an 8x unrolling */ + if (count >= 8) { + /* SkFixed is 16.16 fixed point */ + SkFixed dx4 = dx * 4; + SkFixed dy4 = dy * 4; + SkFixed dx8 = dx * 8; + SkFixed dy8 = dy * 8; + + int32x4_t xbase, ybase; + int32x4_t x2base, y2base; + int16_t *dst16 = (int16_t *) xy; + + /* my sets of maxx/maxy for clamping */ + int32_t maxpair = (maxX&0xffff) | ((maxY&0xffff)<<16); + int16x8_t maxXY = vreinterpretq_s16_s32(vdupq_n_s32(maxpair)); + + /* now build fx/fx+dx/fx+2dx/fx+3dx */ + /* avoid the 'xbase unitialized' warning...*/ + xbase = vdupq_n_s32(fx); + xbase = vsetq_lane_s32(fx+dx, xbase, 1); + xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2); + xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3); + + /* same for fy */ + /* avoid the 'ybase unitialized' warning...*/ + ybase = vdupq_n_s32(fy); + ybase = vsetq_lane_s32(fy+dy, ybase, 1); + ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2); + ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3); + + x2base = vaddq_s32(xbase, vdupq_n_s32(dx4)); + y2base = vaddq_s32(ybase, vdupq_n_s32(dy4)); + + /* store & bump */ + do { + int32x4_t xout, yout; + int32x4_t x2out, y2out; + int16x8_t hi16, hi16_2; + + xout = xbase; + yout = ybase; + + /* overlay y's low16 with hi16 from x */ + /* so we properly shifted xyxyxyxy */ + yout = vsriq_n_s32(yout, xout, 16); + hi16 = vreinterpretq_s16_s32 (yout); + + /* do the clamping; both guys get 0's */ + hi16 = vmaxq_s16 (hi16, vdupq_n_s16(0)); + hi16 = vminq_s16 (hi16, maxXY); + + vst1q_s16 (dst16, hi16); + + /* and for the other 4 pieces of this iteration */ + x2out = x2base; + y2out = y2base; + + /* overlay y's low16 with hi16 from x */ + /* so we properly shifted xyxyxyxy */ + y2out = vsriq_n_s32(y2out, x2out, 16); + hi16_2 = vreinterpretq_s16_s32 (y2out); + + /* do the clamping; both guys get 0's */ + hi16_2 = vmaxq_s16 (hi16_2, vdupq_n_s16(0)); + hi16_2 = vminq_s16 (hi16_2, maxXY); + + /* RBE: gcc regenerates dst16+8 all the time instead + * of folding it into an addressing mode. *sigh* */ + vst1q_s16 (dst16+8, hi16_2); + + /* moving base and on to the next */ + xbase = vaddq_s32 (xbase, vdupq_n_s32 (dx8)); + ybase = vaddq_s32 (ybase, vdupq_n_s32 (dy8)); + x2base = vaddq_s32 (x2base, vdupq_n_s32 (dx8)); + y2base = vaddq_s32 (y2base, vdupq_n_s32 (dy8)); + + dst16 += 16; /* 8x32 aka 16x16 */ + count -= 8; + fx += dx8; + fy += dy8; + } while (count >= 8); + xy = (uint32_t *) dst16; + } + + for (int i = count; i > 0; --i) { + *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX); + fx += dx; fy += dy; + } +} + +static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t* SK_RESTRICT xy, + int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask); + + PREAMBLE(s); + /* max{X,Y} are int here, but later shown/assumed to fit in 16 bits */ + int maxX = s.fBitmap->width() - 1; + int maxY = s.fBitmap->height() - 1; + + SkPerspIter iter(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + + while ((count = iter.next()) != 0) { + const SkFixed* SK_RESTRICT srcXY = iter.getXY(); + + /* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1... + * but we immediately discard the low 16 bits... + * so what we're going to do is vld4, which will give us + * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo' + * parts.... + */ + if (count >= 8) { + int16_t *mysrc = (int16_t *) srcXY; + int16_t *mydst = (int16_t *) xy; + int16x4_t maxX4 = vdup_n_s16((int16_t)maxX); + int16x4_t maxY4 = vdup_n_s16((int16_t)maxY); + int16x4_t zero4 = vdup_n_s16(0); + do { + register int16x4_t xlo asm("d0"); + register int16x4_t xhi asm("d1"); + register int16x4_t ylo asm("d2"); + register int16x4_t yhi asm("d3"); + register int16x4_t x2lo asm("d4"); + register int16x4_t x2hi asm("d5"); + register int16x4_t y2lo asm("d6"); + register int16x4_t y2hi asm("d7"); + + register int16x4_t out_xhi asm("d8"); + register int16x4_t out_yhi asm("d9"); + register int16x4_t out_x2hi asm("d10"); + register int16x4_t out_y2hi asm("d11"); + + + /* vld4 does the de-interleaving for us */ + /* dependent on register assignments above */ + asm ("vld4.16 {d0-d3},[%4] /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */" + : "=w" (xlo), "=w" (xhi), "=w" (ylo), "=w" (yhi) + : "r" (mysrc) + ); + + /* offset == 256 bits == 32 bytes == 8 longs */ + asm ("vld4.16 {d4-d7},[%4,#32] /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */" + : "=w" (x2lo), "=w" (x2hi), "=w" (y2lo), "=w" (y2hi) + : "r" (mysrc) + ); + + /* clamp the first 4 here */ + + /* clamp X>>16 (aka xhi) to 0..maxX */ + xhi = vmax_s16(xhi, zero4); /* now 0.. */ + out_xhi = vmin_s16(xhi, maxX4); /* now 0..maxX */ + + /* clamp Y>>16 (aka yhi) to 0..maxY */ + yhi = vmax_s16(yhi, zero4); /* now 0.. */ + out_yhi = vmin_s16(yhi, maxY4); /* now 0..maxY */ + + /* clamp the second 4 here */ + + /* clamp X>>16 (aka xhi) to 0..maxX */ + x2hi = vmax_s16(x2hi, zero4); /* now 0.. */ + out_x2hi = vmin_s16(x2hi, maxX4); /* now 0..maxX */ + + /* clamp Y>>16 (aka yhi) to 0..maxY */ + y2hi = vmax_s16(y2hi, zero4); /* now 0.. */ + out_y2hi = vmin_s16(y2hi, maxY4); /* now 0..maxY */ + + /* we're storing as {x,y}s: x is [0], y is [1] */ + /* we'll use vst2 to make this happen */ + /* XXX: could use auto increment! */ + asm ("vst2.16 {d8-d9},[%2] /* xlo=%P0 xhi=%P1 */" + : + : "w" (out_xhi), "w" (out_yhi), "r" (mydst) + ); + + /* offset == 16 bytes == 8 shorts */ + asm ("vst2.16 {d10-d11},[%2,#16] /* xlo=%P0 xhi=%P1 */" + : + : "w" (out_x2hi), "w" (out_y2hi), "r" (mydst) + ); + + /* XXX: gcc isn't interleaving these with the NEON ops + * but i think that all the scoreboarding works out */ + count -= 8; /* 8 iterations */ + mysrc += 32; /* 16 longs, aka 32 shorts */ + mydst += 16; /* 16 shorts, aka 8 longs */ + } while (count >= 8); + /* get xy and srcXY fixed up */ + srcXY = (const SkFixed *) mysrc; + xy = (uint32_t *) mydst; + } + + while (--count >= 0) { + *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) | + TILEX_PROCF(srcXY[0], maxX); + srcXY += 2; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max, + SkFixed one PREAMBLE_PARAM_Y) { + unsigned i = TILEY_PROCF(f, max); + i = (i << 4) | TILEY_LOW_BITS(f, max); + return (i << 14) | (TILEY_PROCF((f + one), max)); +} + +static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max, + SkFixed one PREAMBLE_PARAM_X) { + unsigned i = TILEX_PROCF(f, max); + i = (i << 4) | TILEX_LOW_BITS(f, max); + return (i << 14) | (TILEX_PROCF((f + one), max)); +} + +static void SCALE_FILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask)) == 0); + SkASSERT(s.fInvKy == 0); + + PREAMBLE(s); + + const unsigned maxX = s.fBitmap->width() - 1; + const SkFixed one = s.fFilterOneX; + const SkFixed dx = s.fInvSx; + SkFixed fx; + + { + SkPoint pt; + s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &pt); + const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1); + const unsigned maxY = s.fBitmap->height() - 1; + // compute our two Y values up front + *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y); + // now initialize fx + fx = SkScalarToFixed(pt.fX) - (one >> 1); + } + +#ifdef CHECK_FOR_DECAL + // test if we don't need to apply the tile proc + if (dx > 0 && + (unsigned)(fx >> 16) <= maxX && + (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) { + decal_filter_scale(xy, fx, dx, count); + } else +#endif + + if (count >= 4) { + int32x4_t wide_dx, wide_one; + int32x4_t wide_fx, wide_fx1, wide_i, wide_lo; + #if 0 + /* verification hooks -- see below */ + SkFixed debug_fx = fx; + int count_done = 0; + #endif + + wide_fx = vdupq_n_s32(fx); + wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1); + wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2); + wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3); + + wide_dx = vdupq_n_s32(dx); + wide_one = vdupq_n_s32(one); + + while (count >= 4) { + /* original expands to: + * unsigned i = SkClampMax((f) >> 16, max); + * i = (i << 4) | (((f) >> 12) & 0xF); + * return (i << 14) | (SkClampMax(((f + one)) >> 16, max)); + */ + + /* i = SkClampMax(f>>16, maxX) */ + wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0)); + wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX)); + + /* i<<4 | TILEX_LOW_BITS(fx) */ + wide_lo = vshrq_n_s32(wide_fx, 12); + wide_i = vsliq_n_s32(wide_lo, wide_i, 4); + + /* i<<14 */ + wide_i = vshlq_n_s32(wide_i, 14); + + /* SkClampMax(((f + one)) >> 16, max) */ + wide_fx1 = vaddq_s32(wide_fx, wide_one); + wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0)); + wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX)); + + /* final combination */ + wide_i = vorrq_s32(wide_i, wide_fx1); + + vst1q_u32(xy, vreinterpretq_u32_s32(wide_i)); + + #if 0 + /* having a verification hook is a good idea */ + /* use debug_fx, debug_fx+dx, etc. */ + + for (int i=0;i<4;i++) { + uint32_t want = PACK_FILTER_X_NAME(debug_fx, maxX, one PREAMBLE_ARG_X); + if (xy[i] != want) + { + /* print a nastygram */ + SkDebugf("clamp-filter-scale fails\n"); + SkDebugf("got %08x want %08x\n", xy[i], want); + SkDebugf("fx %08x debug_fx %08x dx %08x done %d\n", + fx, debug_fx, dx, count_done); + SkDebugf(" maxX %08x one %08x\n", maxX, one); + + } + debug_fx += dx; + count_done++; + } + #endif + wide_fx += vdupq_n_s32(dx+dx+dx+dx); + fx += dx+dx+dx+dx; + xy += 4; + count -= 4; + } + } + + while (--count >= 0) { + *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X); + fx += dx; + } +} + +static void AFFINE_FILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kAffine_Mask); + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask | + SkMatrix::kAffine_Mask)) == 0); + + PREAMBLE(s); + SkPoint srcPt; + s.fInvProc(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + SkFixed oneX = s.fFilterOneX; + SkFixed oneY = s.fFilterOneY; + SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1); + SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1); + SkFixed dx = s.fInvSx; + SkFixed dy = s.fInvKy; + unsigned maxX = s.fBitmap->width() - 1; + unsigned maxY = s.fBitmap->height() - 1; + + if (count >= 4) { + int32x4_t wide_one, wide_i, wide_lo; + int32x4_t wide_dx, wide_fx, wide_onex, wide_fx1; + int32x4_t wide_dy, wide_fy, wide_oney, wide_fy1; + + /* need side-by-side registers for vst2.32 tricks */ + register int32x4_t wide_x asm("q1"); + register int32x4_t wide_y asm("q0"); + + #undef AFFINE_DEBUG + #if defined(AFFINE_DEBUG) + SkFixed fyp = fy; + SkFixed fxp = fx; + uint32_t *xyp = xy; + int count_done = 0; + #endif + + wide_fx = vdupq_n_s32(fx); + wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1); + wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2); + wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3); + wide_dx = vdupq_n_s32(dx); + + wide_fy = vdupq_n_s32(fy); + wide_fy = vsetq_lane_s32(fy+dy, wide_fy, 1); + wide_fy = vsetq_lane_s32(fy+dy+dy, wide_fy, 2); + wide_fy = vsetq_lane_s32(fy+dy+dy+dy, wide_fy, 3); + wide_dy = vdupq_n_s32(dy); + + wide_onex = vdupq_n_s32(oneX); + wide_oney = vdupq_n_s32(oneY); + + while (count >= 4) { + /* do the X side, then the Y side, then interleave them */ + + /* original expands to: + * unsigned i = SkClampMax((f) >> 16, max); + * i = (i << 4) | (((f) >> 12) & 0xF); + * return (i << 14) | (SkClampMax(((f + one)) >> 16, max)); + */ + + /* i = SkClampMax(f>>16, maxX) */ + wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0)); + wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX)); + + /* i<<4 | TILEX_LOW_BITS(fx) */ + wide_lo = vshrq_n_s32(wide_fx, 12); + wide_i = vsliq_n_s32(wide_lo, wide_i, 4); + + /* i<<14 */ + wide_i = vshlq_n_s32(wide_i, 14); + + /* SkClampMax(((f + one)) >> 16, max) */ + wide_fx1 = vaddq_s32(wide_fx, wide_onex); + wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0)); + wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX)); + + /* final combination */ + wide_x = vorrq_s32(wide_i, wide_fx1); + + /* And now the Y side */ + + /* i = SkClampMax(f>>16, maxX) */ + wide_i = vmaxq_s32(vshrq_n_s32(wide_fy,16), vdupq_n_s32(0)); + wide_i = vminq_s32(wide_i, vdupq_n_s32(maxY)); + + /* i<<4 | TILEX_LOW_BITS(fx) */ + wide_lo = vshrq_n_s32(wide_fy, 12); + wide_i = vsliq_n_s32(wide_lo, wide_i, 4); + + /* i<<14 */ + wide_i = vshlq_n_s32(wide_i, 14); + + /* SkClampMax(((f + one)) >> 16, max) */ + wide_fy1 = vaddq_s32(wide_fy, wide_oney); + wide_fy1 = vmaxq_s32(vshrq_n_s32(wide_fy1,16), vdupq_n_s32(0)); + wide_fy1 = vminq_s32(wide_fy1, vdupq_n_s32(maxY)); + + /* final combination */ + wide_y = vorrq_s32(wide_i, wide_fy1); + + /* interleave as YXYXYXYX as part of the storing */ + asm ("vst2.32 {q0-q1},[%2] /* y=%q0 x=%q1 */" + : + : "w" (wide_y), "w" (wide_x), "r" (xy) + ); + + #if defined(AFFINE_DEBUG) + /* make sure we're good here -- check the 4 we just output */ + for (int i = 0; i<4;i++) { + uint32_t val; + val = PACK_FILTER_Y_NAME(fyp, maxY, oneY PREAMBLE_ARG_Y); + if (val != xy[i*2+0]) { + /* print a nastygram */ + SkDebugf("clamp-filter-affine fails\n"); + SkDebugf("[bad-y] got %08x want %08x\n", xy[i*2+0], val); + SkDebugf("fy %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n", + fy, fxp, fyp, dx, dy, count_done); + SkDebugf(" maxY %08x oneY %08x\n", maxY, oneY); + } + val = PACK_FILTER_X_NAME(fxp, maxX, oneX PREAMBLE_ARG_X); + if (val != xy[i*2+1]) { + /* print a nastygram */ + SkDebugf("clamp-filter-affine fails\n"); + SkDebugf("[bad-x] got %08x want %08x\n", xy[i*2+1], val); + SkDebugf("fx %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n", + fx, fxp, fyp, dx, dy, count_done); + SkDebugf(" maxX %08x one %08x\n", maxX, oneX); + } + fyp += dy; + fxp += dx; + count_done++; + } + #endif + + wide_fx += vdupq_n_s32(dx+dx+dx+dx); + fx += dx+dx+dx+dx; + wide_fy += vdupq_n_s32(dy+dy+dy+dy); + fy += dy+dy+dy+dy; + xy += 8; /* 4 x's, 4 y's */ + count -= 4; + } + } + + while (--count >= 0) { + /* NB: writing Y/X */ + *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y); + fy += dy; + *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X); + fx += dx; + } +} + +static void PERSP_FILTER_NAME(const SkBitmapProcState& s, + uint32_t* SK_RESTRICT xy, int count, + int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask); + + PREAMBLE(s); + unsigned maxX = s.fBitmap->width() - 1; + unsigned maxY = s.fBitmap->height() - 1; + SkFixed oneX = s.fFilterOneX; + SkFixed oneY = s.fFilterOneY; + + SkPerspIter iter(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + + while ((count = iter.next()) != 0) { + const SkFixed* SK_RESTRICT srcXY = iter.getXY(); + + if (count >= 4) { + int32x4_t wide_one, wide_i, wide_lo; + int32x4_t wide_fx1; + int32x4_t wide_fy1; + int32x4_t wide_x, wide_y; + + /* need side-by-side regs for vld2/vst2 tricks */ + register int32x4_t wide_first asm ("q0"); + register int32x4_t wide_second asm ("q1"); + + while (count >= 4) { + /* RBE: it's good, but: + * -- we spill a constant that could be easily regnerated + * [perhaps tweak gcc's NEON constant costs?] + */ + + /* load src: x-y-x-y-x-y-x-y */ + asm ("vld2.32 {q0-q1},[%2] /* x=%q0 y=%q1 */" + : "=w" (wide_first), "=w" (wide_second) + : "r" (srcXY)); + + wide_x = wide_first; + wide_y = wide_second; + + /* do the X side, then the Y side, then interleave them */ + + wide_x = vsubq_s32(wide_x, vdupq_n_s32 (oneX>>1)); + + /* original expands to: + * unsigned i = SkClampMax((f) >> 16, max); + * i = (i << 4) | (((f) >> 12) & 0xF); + * return (i << 14) | (SkClampMax(((f + one)) >> 16, max)); + */ + + /* i = SkClampMax(f>>16, maxX) */ + wide_i = vmaxq_s32 (vshrq_n_s32 (wide_x, 16), vdupq_n_s32 (0)); + wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxX)); + + /* i<<4 | TILEX_LOW_BITS(fx) */ + wide_lo = vshrq_n_s32 (wide_x, 12); + wide_i = vsliq_n_s32 (wide_lo, wide_i, 4); + + /* i<<14 */ + wide_i = vshlq_n_s32 (wide_i, 14); + + /* SkClampMax(((f + one)) >> 16, max) */ + wide_fx1 = vaddq_s32 (wide_x, vdupq_n_s32(oneX)); + wide_fx1 = vmaxq_s32 (vshrq_n_s32 (wide_fx1, 16), vdupq_n_s32 (0)); + wide_fx1 = vminq_s32 (wide_fx1, vdupq_n_s32 (maxX)); + + /* final combination */ + wide_x = vorrq_s32 (wide_i, wide_fx1); + + + /* And now the Y side */ + + wide_y = vsubq_s32(wide_y, vdupq_n_s32 (oneY>>1)); + + /* i = SkClampMax(f>>16, maxX) */ + wide_i = vmaxq_s32 (vshrq_n_s32 (wide_y, 16), vdupq_n_s32 (0)); + wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxY)); + + /* i<<4 | TILEX_LOW_BITS(fx) */ + wide_lo = vshrq_n_s32 (wide_y, 12); + wide_i = vsliq_n_s32 (wide_lo, wide_i, 4); + + /* i<<14 */ + wide_i = vshlq_n_s32 (wide_i, 14); + + /* SkClampMax(((f + one)) >> 16, max) */ + wide_fy1 = vaddq_s32 (wide_y, vdupq_n_s32(oneY)); + wide_fy1 = vmaxq_s32 (vshrq_n_s32 (wide_fy1, 16), vdupq_n_s32 (0)); + wide_fy1 = vminq_s32 (wide_fy1, vdupq_n_s32 (maxY)); + + /* final combination */ + wide_y = vorrq_s32 (wide_i, wide_fy1); + + /* switch them around; have to do it this way to get them + * in the proper registers to match our instruction */ + + /* wide_x/wide_y are fixed regs, in wrong order; swap 'em */ + wide_first = wide_y; + wide_second = wide_x; + + /* iteration bookkeeping, ahead of the asm() for scheduling */ + srcXY += 2*4; + count -= 4; + + /* store interleaved as y-x-y-x-y-x-y-x (NB != read order) */ + asm ("vst2.32 {q0-q1},[%2] /* y=%q0 x=%q1 */" + : + : "w" (wide_first), "w" (wide_second), "r" (xy)); + + /* on to the next iteration */ + /* count, srcXY are handled above */ + xy += 2*4; + } + } + + /* was do-while; NEON code invalidates original count>0 assumption */ + while (--count >= 0) { + /* NB: we read x/y, we write y/x */ + *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY, + oneY PREAMBLE_ARG_Y); + *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX, + oneX PREAMBLE_ARG_X); + srcXY += 2; + } + } +} + +static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = { + SCALE_NOFILTER_NAME, + SCALE_FILTER_NAME, + AFFINE_NOFILTER_NAME, + AFFINE_FILTER_NAME, + PERSP_NOFILTER_NAME, + PERSP_FILTER_NAME +}; + +#undef MAKENAME +#undef TILEX_PROCF +#undef TILEY_PROCF +#ifdef CHECK_FOR_DECAL + #undef CHECK_FOR_DECAL +#endif + +#undef SCALE_NOFILTER_NAME +#undef SCALE_FILTER_NAME +#undef AFFINE_NOFILTER_NAME +#undef AFFINE_FILTER_NAME +#undef PERSP_NOFILTER_NAME +#undef PERSP_FILTER_NAME + +#undef PREAMBLE +#undef PREAMBLE_PARAM_X +#undef PREAMBLE_PARAM_Y +#undef PREAMBLE_ARG_X +#undef PREAMBLE_ARG_Y + +#undef TILEX_LOW_BITS +#undef TILEY_LOW_BITS diff --git a/src/core/SkBitmapProcState_matrix_repeat.h b/src/core/SkBitmapProcState_matrix_repeat.h new file mode 100644 index 0000000000..2697db6a9f --- /dev/null +++ b/src/core/SkBitmapProcState_matrix_repeat.h @@ -0,0 +1,580 @@ +/* NEON optimized code (C) COPYRIGHT 2009 Motorola */ +/* + * Modifications done in-house at Motorola + * + * this is a clone of SkBitmapProcState_matrix.h + * and has been tuned to work with the NEON unit. + * + * Still going back and forth between whether this approach + * (clone the entire SkBitmapProcState_matrix.h file or + * if I should put just the modified routines in here and + * then use a construct like #define DONT_DO_THIS_FUNCTION or + * something like that... + * + * This is for the RepeatX_RepeatY part of the world + */ + + +#if !defined(__ARM_HAVE_NEON) +#error this file can be used only when the NEON unit is enabled +#endif + +#include <arm_neon.h> + +/* + * This has been modified on the knowledge that (at the time) + * we had the following macro definitions in the parent file + * + * #define MAKENAME(suffix) RepeatX_RepeatY ## suffix + * #define TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) + * #define TILEY_PROCF(fy, max) (((fy) & 0xFFFF) * ((max) + 1) >> 16) + * #define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) + * #define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF) + */ + +/* SkClampMax(val,max) -- bound to 0..max */ + +#define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale) +#define SCALE_FILTER_NAME MAKENAME(_filter_scale) +#define AFFINE_NOFILTER_NAME MAKENAME(_nofilter_affine) +#define AFFINE_FILTER_NAME MAKENAME(_filter_affine) +#define PERSP_NOFILTER_NAME MAKENAME(_nofilter_persp) +#define PERSP_FILTER_NAME MAKENAME(_filter_persp) + +#define PACK_FILTER_X_NAME MAKENAME(_pack_filter_x) +#define PACK_FILTER_Y_NAME MAKENAME(_pack_filter_y) + +#ifndef PREAMBLE + #define PREAMBLE(state) + #define PREAMBLE_PARAM_X + #define PREAMBLE_PARAM_Y + #define PREAMBLE_ARG_X + #define PREAMBLE_ARG_Y +#endif + +static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask)) == 0); + + PREAMBLE(s); + // we store y, x, x, x, x, x + + const unsigned maxX = s.fBitmap->width() - 1; + SkFixed fx; + { + SkPoint pt; + s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &pt); + fx = SkScalarToFixed(pt.fY); + const unsigned maxY = s.fBitmap->height() - 1; + *xy++ = TILEY_PROCF(fx, maxY); + fx = SkScalarToFixed(pt.fX); + } + + if (0 == maxX) { + // all of the following X values must be 0 + memset(xy, 0, count * sizeof(uint16_t)); + return; + } + + const SkFixed dx = s.fInvSx; + +#ifdef CHECK_FOR_DECAL + // test if we don't need to apply the tile proc + if ((unsigned)(fx >> 16) <= maxX && + (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) { + decal_nofilter_scale(xy, fx, dx, count); + } else +#endif + { + int i; + +#if defined(__ARM_HAVE_NEON) + /* RBE: very much like done in decal_nofilter , + * but some processing of the 'fx' information + * TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) + */ + if (count >= 8) + { + /* SkFixed is 16.16 fixed point */ + SkFixed dx2 = dx+dx; + SkFixed dx4 = dx2+dx2; + SkFixed dx8 = dx4+dx4; + + /* now build fx/fx+dx/fx+2dx/fx+3dx */ + SkFixed fx1, fx2, fx3; + int32x2_t lower, upper; + int32x4_t lbase, hbase; + int16_t *dst16 = (int16_t *)xy; + + fx1 = fx+dx; + fx2 = fx1+dx; + fx3 = fx2+dx; + +#if 1 + /* avoid the 'lbase unitialized' warning */ + lbase = vdupq_n_s32(fx); +#else + lbase = vsetq_lane_s32(fx, lbase, 0); +#endif + lbase = vsetq_lane_s32(fx1, lbase, 1); + lbase = vsetq_lane_s32(fx2, lbase, 2); + lbase = vsetq_lane_s32(fx3, lbase, 3); + hbase = vaddq_s32(lbase, vdupq_n_s32(dx4)); + + /* store & bump */ + do + { + int32x4_t lout; + int32x4_t hout; + int16x8_t hi16; + + /* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */ + /* mask to low 16 [would like to use uzp tricks) */ + lout = vandq_s32(lbase, vdupq_n_s32(0xffff)); + hout = vandq_s32(hbase, vdupq_n_s32(0xffff)); + /* bare multiplication, not SkFixedMul */ + lout = vmulq_s32(lout, vdupq_n_s32(maxX+1)); + hout = vmulq_s32(hout, vdupq_n_s32(maxX+1)); + + /* extraction, using uzp */ + /* this is ok -- we want all hi(lout)s then all hi(hout)s */ + asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout)); + hi16 = vreinterpretq_s16_s32(hout); + vst1q_s16(dst16, hi16); + + /* bump our base on to the next */ + lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8)); + hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8)); + dst16 += 8; + count -= 8; + fx += dx8; + } while (count >= 8); + xy = (uint32_t *) dst16; + } +#else + /* simple, portable way of looking at 4 at a crack; + * so gets some loop unrolling, but not full SIMD speed + */ + for (i = (count >> 2); i > 0; --i) { + unsigned a, b; + a = TILEX_PROCF(fx, maxX); fx += dx; + b = TILEX_PROCF(fx, maxX); fx += dx; +#ifdef SK_CPU_BENDIAN + *xy++ = (a << 16) | b; +#else + *xy++ = (b << 16) | a; +#endif + a = TILEX_PROCF(fx, maxX); fx += dx; + b = TILEX_PROCF(fx, maxX); fx += dx; +#ifdef SK_CPU_BENDIAN + *xy++ = (a << 16) | b; +#else + *xy++ = (b << 16) | a; +#endif + } + /* loop doesn't adjust count */ + count -= (count>>2); +#endif + uint16_t* xx = (uint16_t*)xy; + for (i = count; i > 0; --i) { + *xx++ = TILEX_PROCF(fx, maxX); fx += dx; + } + } +} + +// note: we could special-case on a matrix which is skewed in X but not Y. +// this would require a more general setup thatn SCALE does, but could use +// SCALE's inner loop that only looks at dx + + +static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kAffine_Mask); + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask | + SkMatrix::kAffine_Mask)) == 0); + + PREAMBLE(s); + SkPoint srcPt; + s.fInvProc(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + SkFixed fx = SkScalarToFixed(srcPt.fX); + SkFixed fy = SkScalarToFixed(srcPt.fY); + SkFixed dx = s.fInvSx; + SkFixed dy = s.fInvKy; + int maxX = s.fBitmap->width() - 1; + int maxY = s.fBitmap->height() - 1; + +#if 1 + int ocount = count; + uint32_t *oxy = xy; + SkFixed bfx = fx, bfy=fy, bdx=dx, bdy=dy; +#endif + +#if defined(__ARM_HAVE_NEON) + + if (0) { extern void rbe(void); rbe(); } + + /* RBE: benchmarks show this eats up time; can we neonize it? */ + /* RBE: very much like done in decal_nofilter , + * but some processing of the 'fx' information + * TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) + */ + if (count >= 4) + { + /* SkFixed is 16.16 fixed point */ + SkFixed dx4 = dx*4; + SkFixed dy4 = dy*4; + + /* now build fx/fx+dx/fx+2dx/fx+3dx */ + int32x2_t lower, upper; + int32x4_t xbase, ybase; + int16_t *dst16 = (int16_t *)xy; + + /* synthesize 4x for both X and Y */ +#if 1 + /* avoid the xbase unitialized warning */ + xbase = vdupq_n_s32(fx); +#else + xbase = vsetq_lane_s32(fx, xbase, 0); +#endif + xbase = vsetq_lane_s32(fx+dx, xbase, 1); + xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2); + xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3); + +#if 1 + /* avoid the xbase unitialized warning */ + ybase = vdupq_n_s32(fy); +#else + ybase = vsetq_lane_s32(fy, ybase, 0); +#endif + ybase = vsetq_lane_s32(fy+dy, ybase, 1); + ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2); + ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3); + + /* store & bump */ + do + { + int32x4_t xout; + int32x4_t yout; + int16x8_t hi16; + + /* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */ + /* mask to low 16 [would like to use uzp tricks) */ + xout = vandq_s32(xbase, vdupq_n_s32(0xffff)); + yout = vandq_s32(ybase, vdupq_n_s32(0xffff)); + /* bare multiplication, not SkFixedMul */ + xout = vmulq_s32(xout, vdupq_n_s32(maxX+1)); + yout = vmulq_s32(yout, vdupq_n_s32(maxY+1)); + + /* put hi16 from xout over low16 from yout */ + yout = vsriq_n_s32(yout, xout, 16); + + /* and then yout has the interleaved upper 16's */ + hi16 = vreinterpretq_s16_s32(yout); + vst1q_s16(dst16, hi16); + + /* bump preserved base & on to the next */ + xbase = vaddq_s32 (xbase, vdupq_n_s32(dx4)); + ybase = vaddq_s32 (ybase, vdupq_n_s32(dy4)); + dst16 += 8; /* 8 x16 aka 4x32 */ + count -= 4; + fx += dx4; + fy += dy4; + } while (count >= 4); + xy = (uint32_t *) dst16; + } +#if 0 + /* diagnostics... see whether we agree with the NEON code */ + int bad = 0; + uint32_t *myxy = oxy; + int myi = (-1); + SkFixed ofx = bfx, ofy= bfy, odx= bdx, ody= bdy; + for (myi = ocount; myi > 0; --myi) { + uint32_t val = (TILEY_PROCF(ofy, maxY) << 16) | TILEX_PROCF(ofx, maxX); + if (val != *myxy++) { + bad++; + break; + } + ofx += odx; ofy += ody; + } + if (bad) { + SkDebugf("repeat-nofilter-affine fails\n"); + SkDebugf("count %d myi %d\n", ocount, myi); + SkDebugf(" bfx %08x, bdx %08x, bfy %08x bdy %08x\n", + bfx, bdx, bfy, bdy); + SkDebugf("maxX %08x maxY %08x\n", maxX, maxY); + } +#endif +#endif + + for (int i = count; i > 0; --i) { + /* fx, fy, dx, dy are all 32 bit 16.16 fixed point */ + /* (((fx) & 0xFFFF) * ((max) + 1) >> 16) */ + *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX); + fx += dx; fy += dy; + } +} + +static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s, + uint32_t* SK_RESTRICT xy, + int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask); + + PREAMBLE(s); + int maxX = s.fBitmap->width() - 1; + int maxY = s.fBitmap->height() - 1; + + SkPerspIter iter(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + + while ((count = iter.next()) != 0) { + const SkFixed* SK_RESTRICT srcXY = iter.getXY(); + +#if defined(__ARM_HAVE_NEON) + /* RBE: */ + /* TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) */ + /* it's a little more complicated than what I did for the + * clamp case -- where I could immediately snip to the top + * 16 bits and do my min/max games there. + * ... might only be able to get 4x unrolling here + */ + + /* vld2 to get a set of 32x4's ... */ + /* do the tile[xy]_procf operations */ + /* which includes doing vuzp to get hi16's */ + /* store it */ + /* -- inner loop (other than vld2) can be had from above */ + + /* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1... + * but we immediately discard the low 16 bits... + * so what we're going to do is vld4, which will give us + * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo' + * parts.... + */ + if (0) { extern void rbe(void); rbe(); } + if (count >= 8) { + int32_t *mysrc = (int32_t *) srcXY; + int16_t *mydst = (int16_t *) xy; + do { + register int32x4_t x asm("q0"); + register int32x4_t y asm("q1"); + register int32x4_t x2 asm("q2"); + register int32x4_t y2 asm("q3"); + + int16x8_t hi; + int16x8_t hi2; + + /* read array of x,y,x,y,x,y */ + /* vld2 does the de-interleaving for us */ + /* dependent on register assignments above */ + asm ("vld2.32 {q0-q1},[%2] /* x=%q0 y=%q1 */" + : "=w" (x), "=w" (y) + : "r" (mysrc) + ); + + /* offset == 256 bits == 32 bytes == 8 longs */ + asm ("vld2.32 {q2-q3},[%2,#32] /* x=%q0 y=%q1 */" + : "=w" (x2), "=w" (y2) + : "r" (mysrc) + ); + + /* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */ + /* mask to low 16 [would like to use uzp tricks) */ + /* bare multiplication, not SkFixedMul */ + x = vandq_s32(x, vdupq_n_s32(0xffff)); + x = vmulq_s32(x, vdupq_n_s32(maxX+1)); + y = vandq_s32(y, vdupq_n_s32(0xffff)); + y = vmulq_s32(y, vdupq_n_s32(maxY+1)); + + x2 = vandq_s32(x2, vdupq_n_s32(0xffff)); + x2 = vmulq_s32(x2, vdupq_n_s32(maxX+1)); + y2 = vandq_s32(y2, vdupq_n_s32(0xffff)); + y2 = vmulq_s32(y2, vdupq_n_s32(maxY+1)); + + /* now collect interleaved high 16's */ + /* (hi-x, hi-y)4 (hi-x2; hi-y2)4 */ + + /* extraction, using uzp, leaves hi16's in y */ + y = vsriq_n_s32(y, x, 16); + hi = vreinterpretq_s16_s32(y); + vst1q_s16(mydst, hi); + + /* and get second 8 bytes out */ + y2 = vsriq_n_s32(y2, x2, 16); + hi2 = vreinterpretq_s16_s32(y2); + vst1q_s16(mydst+8, hi2); + + /* XXX: gcc isn't interleaving these with the NEON ops + * but i think that all the scoreboarding works out */ + count -= 8; /* 8 iterations */ + mysrc += 16; /* 16 longs */ + mydst += 16; /* 16 shorts, aka 8 longs */ + } while (count >= 8); + /* get xy and srcXY fixed up */ + srcXY = (const SkFixed *) mysrc; + xy = (uint32_t *) mydst; + } +#endif + while (--count >= 0) { + *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) | + TILEX_PROCF(srcXY[0], maxX); + srcXY += 2; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max, + SkFixed one PREAMBLE_PARAM_Y) { + unsigned i = TILEY_PROCF(f, max); + i = (i << 4) | TILEY_LOW_BITS(f, max); + return (i << 14) | (TILEY_PROCF((f + one), max)); +} + +static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max, + SkFixed one PREAMBLE_PARAM_X) { + unsigned i = TILEX_PROCF(f, max); + i = (i << 4) | TILEX_LOW_BITS(f, max); + return (i << 14) | (TILEX_PROCF((f + one), max)); +} + +static void SCALE_FILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask)) == 0); + SkASSERT(s.fInvKy == 0); + + PREAMBLE(s); + + const unsigned maxX = s.fBitmap->width() - 1; + const SkFixed one = s.fFilterOneX; + const SkFixed dx = s.fInvSx; + SkFixed fx; + + { + SkPoint pt; + s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &pt); + const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1); + const unsigned maxY = s.fBitmap->height() - 1; + // compute our two Y values up front + *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y); + // now initialize fx + fx = SkScalarToFixed(pt.fX) - (one >> 1); + } + +#ifdef CHECK_FOR_DECAL + // test if we don't need to apply the tile proc + if (dx > 0 && + (unsigned)(fx >> 16) <= maxX && + (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) { + decal_filter_scale(xy, fx, dx, count); + } else +#endif + { + do { + *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X); + fx += dx; + } while (--count != 0); + } +} + +static void AFFINE_FILTER_NAME(const SkBitmapProcState& s, + uint32_t xy[], int count, int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kAffine_Mask); + SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | + SkMatrix::kScale_Mask | + SkMatrix::kAffine_Mask)) == 0); + + PREAMBLE(s); + SkPoint srcPt; + s.fInvProc(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + + SkFixed oneX = s.fFilterOneX; + SkFixed oneY = s.fFilterOneY; + SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1); + SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1); + SkFixed dx = s.fInvSx; + SkFixed dy = s.fInvKy; + unsigned maxX = s.fBitmap->width() - 1; + unsigned maxY = s.fBitmap->height() - 1; + + do { + *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y); + fy += dy; + *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X); + fx += dx; + } while (--count != 0); +} + +static void PERSP_FILTER_NAME(const SkBitmapProcState& s, + uint32_t* SK_RESTRICT xy, int count, + int x, int y) { + SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask); + + extern void rbe(void); + + PREAMBLE(s); + unsigned maxX = s.fBitmap->width() - 1; + unsigned maxY = s.fBitmap->height() - 1; + SkFixed oneX = s.fFilterOneX; + SkFixed oneY = s.fFilterOneY; + + + + SkPerspIter iter(*s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, count); + + while ((count = iter.next()) != 0) { + const SkFixed* SK_RESTRICT srcXY = iter.getXY(); + do { + *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY, + oneY PREAMBLE_ARG_Y); + *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX, + oneX PREAMBLE_ARG_X); + srcXY += 2; + } while (--count != 0); + } +} + +static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = { + SCALE_NOFILTER_NAME, + SCALE_FILTER_NAME, + AFFINE_NOFILTER_NAME, + AFFINE_FILTER_NAME, + PERSP_NOFILTER_NAME, + PERSP_FILTER_NAME +}; + +#undef MAKENAME +#undef TILEX_PROCF +#undef TILEY_PROCF +#ifdef CHECK_FOR_DECAL + #undef CHECK_FOR_DECAL +#endif + +#undef SCALE_NOFILTER_NAME +#undef SCALE_FILTER_NAME +#undef AFFINE_NOFILTER_NAME +#undef AFFINE_FILTER_NAME +#undef PERSP_NOFILTER_NAME +#undef PERSP_FILTER_NAME + +#undef PREAMBLE +#undef PREAMBLE_PARAM_X +#undef PREAMBLE_PARAM_Y +#undef PREAMBLE_ARG_X +#undef PREAMBLE_ARG_Y + +#undef TILEX_LOW_BITS +#undef TILEY_LOW_BITS diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj index 50aee209ca..2f89460357 100644 --- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj +++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 00003C9E0EFC233F000FF73A /* SkDOM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9A0EFC233F000FF73A /* SkDOM.cpp */; }; 00003CA10EFC233F000FF73A /* SkXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */; }; 00003CA40EFC235F000FF73A /* SkXMLParser_empty.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */; }; + 0009E2201057E96800B0DE6F /* SampleStrokePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0009E21F1057E96800B0DE6F /* SampleStrokePath.cpp */; }; 000A99820FD97526007E45BD /* SampleArc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A41E4A0EFC312F00C9CBEB /* SampleArc.cpp */; }; 001B871E1042184D00C84ED4 /* Forth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 001B871D1042184D00C84ED4 /* Forth.cpp */; }; 0028847B0EFAB46A0083E387 /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 002884510EFAA35C0083E387 /* libcore.a */; }; @@ -75,6 +76,7 @@ 00AF9B18103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00AF9B17103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp */; }; 00BB289B104781D00057BF7E /* SampleForth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00BB289A104781D00057BF7E /* SampleForth.cpp */; }; 00C1B809103857A400FA5948 /* SampleFillType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0041CE270F00A12400695E8C /* SampleFillType.cpp */; }; + 00EB4593104DBB18002B413E /* ForthTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00EB4592104DBB18002B413E /* ForthTests.cpp */; }; 00ED55F3104A10EB00F51FF8 /* StdWords.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00ED55F2104A10EB00F51FF8 /* StdWords.cpp */; }; 00F53F480FFCFC4D003FA70A /* SampleGradients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */; }; 00FF39140FC6ED2C00915187 /* SampleEffects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00FF39130FC6ED2C00915187 /* SampleEffects.cpp */; }; @@ -153,6 +155,7 @@ 00003C9A0EFC233F000FF73A /* SkDOM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkDOM.cpp; path = ../../src/xml/SkDOM.cpp; sourceTree = SOURCE_ROOT; }; 00003C9D0EFC233F000FF73A /* SkXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser.cpp; path = ../../src/xml/SkXMLParser.cpp; sourceTree = SOURCE_ROOT; }; 00003CA30EFC235F000FF73A /* SkXMLParser_empty.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXMLParser_empty.cpp; path = ../../src/ports/SkXMLParser_empty.cpp; sourceTree = SOURCE_ROOT; }; + 0009E21F1057E96800B0DE6F /* SampleStrokePath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleStrokePath.cpp; path = ../../samplecode/SampleStrokePath.cpp; sourceTree = SOURCE_ROOT; }; 001B871D1042184D00C84ED4 /* Forth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Forth.cpp; path = ../../forth/Forth.cpp; sourceTree = SOURCE_ROOT; }; 002884490EFAA35C0083E387 /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = core.xcodeproj; path = ../core/core.xcodeproj; sourceTree = SOURCE_ROOT; }; 002884B40EFAB69F0083E387 /* maccore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = maccore.xcodeproj; path = ../maccore/maccore.xcodeproj; sourceTree = SOURCE_ROOT; }; @@ -208,6 +211,7 @@ 00BB289A104781D00057BF7E /* SampleForth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleForth.cpp; path = ../../forth/SampleForth.cpp; sourceTree = SOURCE_ROOT; }; 00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleGradients.cpp; path = ../../samplecode/SampleGradients.cpp; sourceTree = SOURCE_ROOT; }; 00D6B5CB0F72DC4300C466B9 /* SampleFuzz.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleFuzz.cpp; path = ../../samplecode/SampleFuzz.cpp; sourceTree = SOURCE_ROOT; }; + 00EB4592104DBB18002B413E /* ForthTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ForthTests.cpp; path = ../../forth/ForthTests.cpp; sourceTree = SOURCE_ROOT; }; 00ED55F2104A10EB00F51FF8 /* StdWords.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StdWords.cpp; path = ../../forth/StdWords.cpp; sourceTree = SOURCE_ROOT; }; 00FF39130FC6ED2C00915187 /* SampleEffects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleEffects.cpp; path = ../../samplecode/SampleEffects.cpp; sourceTree = SOURCE_ROOT; }; 0156F80307C56A3000C6122B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; @@ -289,6 +293,7 @@ 0041CE270F00A12400695E8C /* SampleFillType.cpp */, 00AF9B17103CD5EB00CBBCB3 /* SampleDitherBitmap.cpp */, 00840B74104C69F5005B3EDC /* SampleExtractAlpha.cpp */, + 0009E21F1057E96800B0DE6F /* SampleStrokePath.cpp */, 0041CE280F00A12400695E8C /* SampleFilter.cpp */, 0041CE290F00A12400695E8C /* SampleFilter2.cpp */, 0041CE2A0F00A12400695E8C /* SampleFontCache.cpp */, @@ -366,6 +371,7 @@ children = ( 001B871D1042184D00C84ED4 /* Forth.cpp */, 00ED55F2104A10EB00F51FF8 /* StdWords.cpp */, + 00EB4592104DBB18002B413E /* ForthTests.cpp */, 00BB289A104781D00057BF7E /* SampleForth.cpp */, 2762F66A0FCCCAA2002BD8B4 /* images */, 00003C6A0EFC22AD000FF73A /* views */, @@ -595,6 +601,8 @@ 00BB289B104781D00057BF7E /* SampleForth.cpp in Sources */, 00ED55F3104A10EB00F51FF8 /* StdWords.cpp in Sources */, 00840B75104C69F5005B3EDC /* SampleExtractAlpha.cpp in Sources */, + 00EB4593104DBB18002B413E /* ForthTests.cpp in Sources */, + 0009E2201057E96800B0DE6F /* SampleStrokePath.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |