static void testTightBoundsLines(PathOpsThreadState* data) { SkRandom ran; for (int index = 0; index < 1000; ++index) { SkPath path; int contourCount = ran.nextRangeU(1, 10); for (int cIndex = 0; cIndex < contourCount; ++cIndex) { int lineCount = ran.nextRangeU(1, 10); path.moveTo(ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)); for (int lIndex = 0; lIndex < lineCount; ++lIndex) { path.lineTo(ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)); } if (ran.nextBool()) { path.close(); } } SkRect classicBounds = path.getBounds(); SkRect tightBounds; REPORTER_ASSERT(data->fReporter, TightBounds(path, &tightBounds)); REPORTER_ASSERT(data->fReporter, classicBounds == tightBounds); } }
static void TestTLList(skiatest::Reporter* reporter) { typedef SkTLList<ListElement> ElList; typedef ElList::Iter Iter; SkRandom random; for (int i = 1; i <= 16; i *= 2) { ElList list1(i); ElList list2(i); Iter iter1; Iter iter2; Iter iter3; Iter iter4; #if SK_ENABLE_INST_COUNT SkASSERT(0 == ListElement::InstanceCount()); #endif REPORTER_ASSERT(reporter, list1.isEmpty()); REPORTER_ASSERT(reporter, NULL == iter1.init(list1, Iter::kHead_IterStart)); REPORTER_ASSERT(reporter, NULL == iter1.init(list1, Iter::kTail_IterStart)); // Try popping an empty list list1.popHead(); list1.popTail(); REPORTER_ASSERT(reporter, list1.isEmpty()); REPORTER_ASSERT(reporter, list1 == list2); // Create two identical lists, one by appending to head and the other to the tail. list1.addToHead(ListElement(1)); list2.addToTail(ListElement(1)); #if SK_ENABLE_INST_COUNT SkASSERT(2 == ListElement::InstanceCount()); #endif iter1.init(list1, Iter::kHead_IterStart); iter2.init(list1, Iter::kTail_IterStart); REPORTER_ASSERT(reporter, iter1.get()->fID == iter2.get()->fID); iter3.init(list2, Iter::kHead_IterStart); iter4.init(list2, Iter::kTail_IterStart); REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID); REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID); REPORTER_ASSERT(reporter, list1 == list2); list2.reset(); // use both before/after in-place construction on an empty list SkNEW_INSERT_IN_LLIST_BEFORE(&list2, list2.headIter(), ListElement, (1)); REPORTER_ASSERT(reporter, list2 == list1); list2.reset(); SkNEW_INSERT_IN_LLIST_AFTER(&list2, list2.tailIter(), ListElement, (1)); REPORTER_ASSERT(reporter, list2 == list1); // add an element to the second list, check that iters are still valid iter3.init(list2, Iter::kHead_IterStart); iter4.init(list2, Iter::kTail_IterStart); list2.addToHead(ListElement(2)); #if SK_ENABLE_INST_COUNT SkASSERT(3 == ListElement::InstanceCount()); #endif REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID); REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID); REPORTER_ASSERT(reporter, 1 == Iter(list2, Iter::kTail_IterStart).get()->fID); REPORTER_ASSERT(reporter, 2 == Iter(list2, Iter::kHead_IterStart).get()->fID); REPORTER_ASSERT(reporter, list1 != list2); list1.addToHead(ListElement(2)); REPORTER_ASSERT(reporter, list1 == list2); #if SK_ENABLE_INST_COUNT SkASSERT(4 == ListElement::InstanceCount()); #endif REPORTER_ASSERT(reporter, !list1.isEmpty()); list1.reset(); list2.reset(); #if SK_ENABLE_INST_COUNT SkASSERT(0 == ListElement::InstanceCount()); #endif REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty()); // randomly perform insertions and deletions on a list and perform tests int count = 0; for (int j = 0; j < 100; ++j) { if (list1.isEmpty() || random.nextBiasedBool(3 * SK_Scalar1 / 4)) { int id = j; // Choose one of three ways to insert a new element: at the head, at the tail, // before a random element, after a random element int numValidMethods = 0 == count ? 2 : 4; int insertionMethod = random.nextULessThan(numValidMethods); switch (insertionMethod) { case 0: list1.addToHead(ListElement(id)); break; case 1: list1.addToTail(ListElement(id)); break; case 2: // fallthru to share code that picks random element. case 3: { int n = random.nextULessThan(list1.count()); Iter iter = list1.headIter(); // remember the elements before/after the insertion point. while (n--) { iter.next(); } Iter prev(iter); Iter next(iter); next.next(); prev.prev(); SkASSERT(NULL != iter.get()); // insert either before or after the iterator, then check that the // surrounding sequence is correct. if (2 == insertionMethod) { SkNEW_INSERT_IN_LLIST_BEFORE(&list1, iter, ListElement, (id)); Iter newItem(iter); newItem.prev(); REPORTER_ASSERT(reporter, newItem.get()->fID == id); if (NULL != next.get()) { REPORTER_ASSERT(reporter, next.prev()->fID == iter.get()->fID); } if (NULL != prev.get()) { REPORTER_ASSERT(reporter, prev.next()->fID == id); } } else { SkNEW_INSERT_IN_LLIST_AFTER(&list1, iter, ListElement, (id)); Iter newItem(iter); newItem.next(); REPORTER_ASSERT(reporter, newItem.get()->fID == id); if (NULL != next.get()) { REPORTER_ASSERT(reporter, next.prev()->fID == id); } if (NULL != prev.get()) { REPORTER_ASSERT(reporter, prev.next()->fID == iter.get()->fID); } } } } ++count; } else { // walk to a random place either forward or backwards and remove. int n = random.nextULessThan(list1.count()); Iter::IterStart start; ListElement* (Iter::*incrFunc)(); if (random.nextBool()) { start = Iter::kHead_IterStart; incrFunc = &Iter::next; } else { start = Iter::kTail_IterStart; incrFunc = &Iter::prev; } // find the element Iter iter(list1, start); while (n--) { REPORTER_ASSERT(reporter, NULL != iter.get()); (iter.*incrFunc)(); } REPORTER_ASSERT(reporter, NULL != iter.get()); // remember the prev and next elements from the element to be removed Iter prev = iter; Iter next = iter; prev.prev(); next.next(); list1.remove(iter.get()); // make sure the remembered next/prev iters still work Iter pn = prev; pn.next(); Iter np = next; np.prev(); // pn should match next unless the target node was the head, in which case prev // walked off the list. REPORTER_ASSERT(reporter, pn.get() == next.get() || NULL == prev.get()); // Similarly, np should match prev unless next originally walked off the tail. REPORTER_ASSERT(reporter, np.get() == prev.get() || NULL == next.get()); --count; } REPORTER_ASSERT(reporter, count == list1.count()); #if SK_ENABLE_INST_COUNT SkASSERT(count == ListElement::InstanceCount()); #endif } list1.reset(); #if SK_ENABLE_INST_COUNT SkASSERT(0 == ListElement::InstanceCount()); #endif } }
bool GrGpuGL::programUnitTest(int maxStages) { GrTextureDesc dummyDesc; dummyDesc.fFlags = kRenderTarget_GrTextureFlagBit; dummyDesc.fConfig = kSkia8888_GrPixelConfig; dummyDesc.fWidth = 34; dummyDesc.fHeight = 18; SkAutoTUnref<GrTexture> dummyTexture1(this->createTexture(dummyDesc, NULL, 0)); dummyDesc.fFlags = kNone_GrTextureFlags; dummyDesc.fConfig = kAlpha_8_GrPixelConfig; dummyDesc.fWidth = 16; dummyDesc.fHeight = 22; SkAutoTUnref<GrTexture> dummyTexture2(this->createTexture(dummyDesc, NULL, 0)); if (!dummyTexture1 || ! dummyTexture2) { return false; } static const int NUM_TESTS = 512; SkRandom random; for (int t = 0; t < NUM_TESTS; ++t) { #if 0 GrPrintf("\nTest Program %d\n-------------\n", t); static const int stop = -1; if (t == stop) { int breakpointhere = 9; } #endif GrGLProgramDesc pdesc; int currAttribIndex = 1; // we need to always leave room for position int currTextureCoordSet = 0; GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()}; int numStages = random.nextULessThan(maxStages + 1); int numColorStages = random.nextULessThan(numStages + 1); int numCoverageStages = numStages - numColorStages; SkAutoSTMalloc<8, const GrFragmentStage*> stages(numStages); bool usePathRendering = this->glCaps().pathRenderingSupport() && random.nextBool(); GrGpu::DrawType drawType = usePathRendering ? GrGpu::kDrawPath_DrawType : GrGpu::kDrawPoints_DrawType; SkAutoTDelete<GrGeometryStage> geometryProcessor; bool hasGeometryProcessor = usePathRendering ? false : random.nextBool(); if (hasGeometryProcessor) { while (true) { SkAutoTUnref<const GrGeometryProcessor> effect( GrProcessorTestFactory<GrGeometryProcessor>::CreateStage(&random, this->getContext(), *this->caps(), dummyTextures)); SkASSERT(effect); // Only geometryProcessor can use vertex shader GrGeometryStage* stage = SkNEW_ARGS(GrGeometryStage, (effect.get())); geometryProcessor.reset(stage); // we have to set dummy vertex attribs const GrGeometryProcessor::VertexAttribArray& v = effect->getVertexAttribs(); int numVertexAttribs = v.count(); SkASSERT(GrGeometryProcessor::kMaxVertexAttribs == 2 && GrGeometryProcessor::kMaxVertexAttribs >= numVertexAttribs); size_t runningStride = GrVertexAttribTypeSize(genericVertexAttribs[0].fType); for (int i = 0; i < numVertexAttribs; i++) { genericVertexAttribs[i + 1].fOffset = runningStride; genericVertexAttribs[i + 1].fType = convert_sltype_to_attribtype(v[i].getType()); runningStride += GrVertexAttribTypeSize(genericVertexAttribs[i + 1].fType); } // update the vertex attributes with the ds GrDrawState* ds = this->drawState(); ds->setVertexAttribs<genericVertexAttribs>(numVertexAttribs + 1, runningStride); currAttribIndex = numVertexAttribs + 1; break; } } for (int s = 0; s < numStages;) { SkAutoTUnref<const GrFragmentProcessor> effect( GrProcessorTestFactory<GrFragmentProcessor>::CreateStage( &random, this->getContext(), *this->caps(), dummyTextures)); SkASSERT(effect); // If adding this effect would exceed the max texture coord set count then generate a // new random effect. if (usePathRendering && this->glPathRendering()->texturingMode() == GrGLPathRendering::FixedFunction_TexturingMode) {; int numTransforms = effect->numTransforms(); if (currTextureCoordSet + numTransforms > this->glCaps().maxFixedFunctionTextureCoords()) { continue; } currTextureCoordSet += numTransforms; } GrFragmentStage* stage = SkNEW_ARGS(GrFragmentStage, (effect.get())); stages[s] = stage; ++s; } const GrTexture* dstTexture = random.nextBool() ? dummyTextures[0] : dummyTextures[1]; if (!pdesc.setRandom(&random, this, dummyTextures[0]->asRenderTarget(), dstTexture, geometryProcessor.get(), stages.get(), numColorStages, numCoverageStages, currAttribIndex, drawType)) { return false; } SkAutoTUnref<GrOptDrawState> optState(GrOptDrawState::Create(this->getDrawState(), *this->caps(), drawType)); SkAutoTUnref<GrGLProgram> program( GrGLProgramBuilder::CreateProgram(*optState, pdesc, drawType, geometryProcessor, stages, stages + numColorStages, this)); for (int s = 0; s < numStages; ++s) { SkDELETE(stages[s]); } if (NULL == program.get()) { return false; } // We have to reset the drawstate because we might have added a gp this->drawState()->reset(); } return true; }
bool GrGpuGL::programUnitTest(int maxStages) { GrTextureDesc dummyDesc; dummyDesc.fFlags = kRenderTarget_GrTextureFlagBit; dummyDesc.fConfig = kSkia8888_GrPixelConfig; dummyDesc.fWidth = 34; dummyDesc.fHeight = 18; SkAutoTUnref<GrTexture> dummyTexture1(this->createTexture(dummyDesc, NULL, 0)); dummyDesc.fFlags = kNone_GrTextureFlags; dummyDesc.fConfig = kAlpha_8_GrPixelConfig; dummyDesc.fWidth = 16; dummyDesc.fHeight = 22; SkAutoTUnref<GrTexture> dummyTexture2(this->createTexture(dummyDesc, NULL, 0)); static const int NUM_TESTS = 512; SkRandom random; for (int t = 0; t < NUM_TESTS; ++t) { #if 0 GrPrintf("\nTest Program %d\n-------------\n", t); static const int stop = -1; if (t == stop) { int breakpointhere = 9; } #endif GrGLProgramDesc pdesc; int currAttribIndex = 1; // we need to always leave room for position int currTextureCoordSet = 0; int attribIndices[2] = { 0, 0 }; GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()}; int numStages = random.nextULessThan(maxStages + 1); int numColorStages = random.nextULessThan(numStages + 1); int numCoverageStages = numStages - numColorStages; SkAutoSTMalloc<8, const GrEffectStage*> stages(numStages); bool useFixedFunctionTexturing = this->shouldUseFixedFunctionTexturing(); for (int s = 0; s < numStages;) { SkAutoTUnref<const GrEffectRef> effect(GrEffectTestFactory::CreateStage( &random, this->getContext(), *this->caps(), dummyTextures)); SkASSERT(effect); int numAttribs = (*effect)->numVertexAttribs(); // If adding this effect would exceed the max attrib count then generate a // new random effect. if (currAttribIndex + numAttribs > GrDrawState::kMaxVertexAttribCnt) { continue; } // If adding this effect would exceed the max texture coord set count then generate a // new random effect. if (useFixedFunctionTexturing && !(*effect)->hasVertexCode()) { int numTransforms = (*effect)->numTransforms(); if (currTextureCoordSet + numTransforms > this->glCaps().maxFixedFunctionTextureCoords()) { continue; } currTextureCoordSet += numTransforms; } useFixedFunctionTexturing = useFixedFunctionTexturing && !(*effect)->hasVertexCode(); for (int i = 0; i < numAttribs; ++i) { attribIndices[i] = currAttribIndex++; } GrEffectStage* stage = SkNEW_ARGS(GrEffectStage, (effect.get(), attribIndices[0], attribIndices[1])); stages[s] = stage; ++s; } const GrTexture* dstTexture = random.nextBool() ? dummyTextures[0] : dummyTextures[1]; pdesc.setRandom(&random, this, dummyTextures[0]->asRenderTarget(), dstTexture, stages.get(), numColorStages, numCoverageStages, currAttribIndex); SkAutoTUnref<GrGLProgram> program(GrGLProgram::Create(this, pdesc, stages, stages + numColorStages)); for (int s = 0; s < numStages; ++s) { SkDELETE(stages[s]); } if (NULL == program.get()) { return false; } } return true; }
static void testTightBoundsQuads(PathOpsThreadState* data) { SkRandom ran; const int bitWidth = 32; const int bitHeight = 32; const float pathMin = 1; const float pathMax = (float) (bitHeight - 2); SkBitmap& bits = *data->fBitmap; if (bits.width() == 0) { bits.allocN32Pixels(bitWidth, bitHeight); } SkCanvas canvas(bits); SkPaint paint; for (int index = 0; index < 100; ++index) { SkPath path; int contourCount = ran.nextRangeU(1, 10); for (int cIndex = 0; cIndex < contourCount; ++cIndex) { int lineCount = ran.nextRangeU(1, 10); path.moveTo(ran.nextRangeF(1, pathMax), ran.nextRangeF(pathMin, pathMax)); for (int lIndex = 0; lIndex < lineCount; ++lIndex) { if (ran.nextBool()) { path.lineTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax)); } else { path.quadTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax)); } } if (ran.nextBool()) { path.close(); } } SkRect classicBounds = path.getBounds(); SkRect tightBounds; REPORTER_ASSERT(data->fReporter, TightBounds(path, &tightBounds)); REPORTER_ASSERT(data->fReporter, classicBounds.contains(tightBounds)); canvas.drawColor(SK_ColorWHITE); canvas.drawPath(path, paint); SkIRect bitsWritten = {31, 31, 0, 0}; for (int y = 0; y < bitHeight; ++y) { uint32_t* addr1 = data->fBitmap->getAddr32(0, y); bool lineWritten = false; for (int x = 0; x < bitWidth; ++x) { if (addr1[x] == (uint32_t) -1) { continue; } lineWritten = true; bitsWritten.fLeft = SkTMin(bitsWritten.fLeft, x); bitsWritten.fRight = SkTMax(bitsWritten.fRight, x); } if (!lineWritten) { continue; } bitsWritten.fTop = SkTMin(bitsWritten.fTop, y); bitsWritten.fBottom = SkTMax(bitsWritten.fBottom, y); } if (!bitsWritten.isEmpty()) { SkIRect tightOut; tightBounds.roundOut(&tightOut); REPORTER_ASSERT(data->fReporter, tightOut.contains(bitsWritten)); } } }