void GrInOrderDrawBuffer::onDrawPaths(int pathCount, const GrPath** paths, const SkMatrix* transforms, SkPath::FillType fill, SkStrokeRec::Style stroke, const GrDeviceCoordTexture* dstCopy) { SkASSERT(pathCount); if (this->needsNewClip()) { this->recordClip(); } if (this->needsNewState()) { this->recordState(); } DrawPaths* dp = this->recordDrawPaths(); dp->fPathCount = pathCount; dp->fPaths = SkNEW_ARRAY(const GrPath*, pathCount); memcpy(dp->fPaths, paths, sizeof(GrPath*) * pathCount); for (int i = 0; i < pathCount; ++i) { dp->fPaths[i]->ref(); } dp->fTransforms = SkNEW_ARRAY(SkMatrix, pathCount); memcpy(dp->fTransforms, transforms, sizeof(SkMatrix) * pathCount); dp->fFill = fill; dp->fStroke = stroke; if (NULL != dstCopy) { dp->fDstCopy = *dstCopy; } }
const GrIndexBuffer* GrResourceProvider::createInstancedIndexBuffer(const uint16_t* pattern, int patternSize, int reps, int vertCount, const GrUniqueKey& key) { size_t bufferSize = patternSize * reps * sizeof(uint16_t); GrIndexBuffer* buffer = this->getIndexBuffer(bufferSize, /* dynamic = */ false, true); if (!buffer) { return NULL; } uint16_t* data = (uint16_t*) buffer->map(); bool useTempData = (NULL == data); if (useTempData) { data = SkNEW_ARRAY(uint16_t, reps * patternSize); } for (int i = 0; i < reps; ++i) { int baseIdx = i * patternSize; uint16_t baseVert = (uint16_t)(i * vertCount); for (int j = 0; j < patternSize; ++j) { data[baseIdx+j] = baseVert + pattern[j]; } } if (useTempData) { if (!buffer->updateData(data, bufferSize)) { buffer->unref(); return NULL; } SkDELETE_ARRAY(data); } else { buffer->unmap(); } this->assignUniqueKeyToResource(key, buffer); return buffer; }
static uint32_t setup_quad_index_buffer(const GrGLInterface* gl) { static const int kMaxQuads = 1;//1 << 12; // max possible: (1 << 14) - 1; GR_STATIC_ASSERT(4 * kMaxQuads <= 65535); static const uint16_t kPattern[] = { 0, 1, 2, 0, 2, 3 }; static const int kPatternSize = 6; static const int kVertCount = 4; static const int kIndicesCount = kPatternSize * kMaxQuads; int size = kPatternSize * kMaxQuads * sizeof(uint16_t); uint16_t* data = SkNEW_ARRAY(uint16_t, kMaxQuads * kPatternSize); for (int i = 0; i < kMaxQuads; ++i) { int baseIdx = i * kPatternSize; uint16_t baseVert = (uint16_t)(i * kVertCount); for (int j = 0; j < kPatternSize; ++j) { data[baseIdx+j] = baseVert + kPattern[j]; } } GrGLuint quadIBO; GR_GL_CALL(gl, GenBuffers(1, &quadIBO)); GR_GL_CALL(gl, BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, quadIBO)); GR_GL_CALL(gl, BufferData(GR_GL_ELEMENT_ARRAY_BUFFER, size, data, GR_GL_STATIC_DRAW)); SkDELETE_ARRAY(data); return kIndicesCount; }
GrIndexBuffer* GrGpu::createInstancedIndexBuffer(const uint16_t* pattern, int patternSize, int reps, int vertCount, bool isDynamic) { size_t bufferSize = patternSize * reps * sizeof(uint16_t); GrGpu* me = const_cast<GrGpu*>(this); GrIndexBuffer* buffer = me->createIndexBuffer(bufferSize, isDynamic); if (buffer) { uint16_t* data = (uint16_t*) buffer->map(); bool useTempData = (NULL == data); if (useTempData) { data = SkNEW_ARRAY(uint16_t, reps * patternSize); } for (int i = 0; i < reps; ++i) { int baseIdx = i * patternSize; uint16_t baseVert = (uint16_t)(i * vertCount); for (int j = 0; j < patternSize; ++j) { data[baseIdx+j] = baseVert + pattern[j]; } } if (useTempData) { if (!buffer->updateData(data, bufferSize)) { SkFAIL("Can't get indices into buffer!"); } SkDELETE_ARRAY(data); } else { buffer->unmap(); } } return buffer; }
GrIndexBuffer* GrAARectRenderer::aaFillRectIndexBuffer(GrGpu* gpu) { static const size_t kAAFillRectIndexBufferSize = kIndicesPerAAFillRect * sizeof(uint16_t) * kNumAAFillRectsInIndexBuffer; if (NULL == fAAFillRectIndexBuffer) { fAAFillRectIndexBuffer = gpu->createIndexBuffer(kAAFillRectIndexBufferSize, false); if (NULL != fAAFillRectIndexBuffer) { uint16_t* data = (uint16_t*) fAAFillRectIndexBuffer->lock(); bool useTempData = (NULL == data); if (useTempData) { data = SkNEW_ARRAY(uint16_t, kNumAAFillRectsInIndexBuffer * kIndicesPerAAFillRect); } for (int i = 0; i < kNumAAFillRectsInIndexBuffer; ++i) { // Each AA filled rect is drawn with 8 vertices and 10 triangles (8 around // the inner rect (for AA) and 2 for the inner rect. int baseIdx = i * kIndicesPerAAFillRect; uint16_t baseVert = (uint16_t)(i * kVertsPerAAFillRect); for (int j = 0; j < kIndicesPerAAFillRect; ++j) { data[baseIdx+j] = baseVert + gFillAARectIdx[j]; } } if (useTempData) { if (!fAAFillRectIndexBuffer->updateData(data, kAAFillRectIndexBufferSize)) { GrCrash("Can't get AA Fill Rect indices into buffer!"); } SkDELETE_ARRAY(data); } else { fAAFillRectIndexBuffer->unlock(); } } } return fAAFillRectIndexBuffer; }
SkTileGrid::SkTileGrid(int xTiles, int yTiles, const SkTileGridFactory::TileGridInfo& info) : fXTiles(xTiles) , fYTiles(yTiles) , fInvWidth( SkScalarInvert(info.fTileInterval.width())) , fInvHeight(SkScalarInvert(info.fTileInterval.height())) , fMarginWidth (info.fMargin.fWidth +1) // Margin is offset by 1 as a provision for AA and , fMarginHeight(info.fMargin.fHeight+1) // to cancel the outset applied by getClipDeviceBounds. , fOffset(SkPoint::Make(info.fOffset.fX, info.fOffset.fY)) , fGridBounds(SkRect::MakeWH(xTiles * info.fTileInterval.width(), yTiles * info.fTileInterval.height())) , fTiles(SkNEW_ARRAY(SkTDArray<unsigned>, xTiles * yTiles)) {}
SkTileGrid::SkTileGrid(int xTiles, int yTiles, const SkTileGridFactory::TileGridInfo& info) : fXTiles(xTiles) , fYTiles(yTiles) , fInfo(info) , fCount(0) , fTiles(SkNEW_ARRAY(SkTDArray<Entry>, xTiles * yTiles)) { // Margin is offset by 1 as a provision for AA and // to cancel-out the outset applied by getClipDeviceBounds. fInfo.fMargin.fHeight++; fInfo.fMargin.fWidth++; }
SkTileGrid::SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount) { fTileWidth = tileWidth; fTileHeight = tileHeight; fXTileCount = xTileCount; fYTileCount = yTileCount; fTileCount = fXTileCount * fYTileCount; fInsertionCount = 0; fGridBounds = SkIRect::MakeXYWH(0, 0, fTileWidth * fXTileCount, fTileHeight * fYTileCount); fTileData = SkNEW_ARRAY(SkTDArray<void *>, fTileCount); }
GrTextureStripAtlas::GrTextureStripAtlas(GrTextureStripAtlas::Desc desc) : fCacheKey(sk_atomic_inc(&gCacheCount)) , fLockedRows(0) , fDesc(desc) , fNumRows(desc.fHeight / desc.fRowHeight) , fTexture(NULL) , fRows(SkNEW_ARRAY(AtlasRow, fNumRows)) , fLRUFront(NULL) , fLRUBack(NULL) { GrAssert(fNumRows * fDesc.fRowHeight == fDesc.fHeight); this->initLRU(); VALIDATE; }
/* * Creates an instance of the decoder * Called only by NewFromStream */ SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, uint32_t offset, SkBmpCodec::RowOrder rowOrder, size_t RLEBytes) : INHERITED(info, stream, bitsPerPixel, rowOrder) , fColorTable(NULL) , fNumColors(this->computeNumColors(numColors)) , fBytesPerColor(bytesPerColor) , fOffset(offset) , fStreamBuffer(SkNEW_ARRAY(uint8_t, RLEBytes)) , fRLEBytes(RLEBytes) , fCurrRLEByte(0) {}
SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { fKernelSize.fWidth = buffer.readInt(); fKernelSize.fHeight = buffer.readInt(); uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight; fKernel = SkNEW_ARRAY(SkScalar, size); SkDEBUGCODE(uint32_t readSize = )buffer.readScalarArray(fKernel); SkASSERT(readSize == size); fGain = buffer.readScalar(); fBias = buffer.readScalar(); fTarget.fX = buffer.readInt(); fTarget.fY = buffer.readInt(); fTileMode = (TileMode) buffer.readInt(); fConvolveAlpha = buffer.readBool(); }
GrTextureStripAtlas::GrTextureStripAtlas(GrTextureStripAtlas::Desc desc) : fCacheKey(sk_atomic_inc(&gCacheCount)) , fLockedRows(0) , fDesc(desc) , fNumRows(desc.fHeight / desc.fRowHeight) , fTexture(NULL) , fRows(SkNEW_ARRAY(AtlasRow, fNumRows)) , fLRUFront(NULL) , fLRUBack(NULL) { SkASSERT(fNumRows * fDesc.fRowHeight == fDesc.fHeight); this->initLRU(); fNormalizedYHeight = SK_Scalar1 / fDesc.fHeight; VALIDATE; }
SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, bool convolveAlpha, SkImageFilter* input) : INHERITED(input), fKernelSize(kernelSize), fGain(gain), fBias(bias), fTarget(target), fTileMode(tileMode), fConvolveAlpha(convolveAlpha) { uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight; fKernel = SkNEW_ARRAY(SkScalar, size); memcpy(fKernel, kernel, size * sizeof(SkScalar)); SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1); SkASSERT(target.fX >= 0 && target.fX < kernelSize.fWidth); SkASSERT(target.fY >= 0 && target.fY < kernelSize.fHeight); }
SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(1, buffer) { // We need to be able to read at most SK_MaxS32 bytes, so divide that // by the size of a scalar to know how many scalars we can read. static const int32_t kMaxSize = SK_MaxS32 / sizeof(SkScalar); fKernelSize.fWidth = buffer.readInt(); fKernelSize.fHeight = buffer.readInt(); if ((fKernelSize.fWidth >= 1) && (fKernelSize.fHeight >= 1) && // Make sure size won't be larger than a signed int, // which would still be extremely large for a kernel, // but we don't impose a hard limit for kernel size (kMaxSize / fKernelSize.fWidth >= fKernelSize.fHeight)) { size_t size = fKernelSize.fWidth * fKernelSize.fHeight; fKernel = SkNEW_ARRAY(SkScalar, size); SkDEBUGCODE(bool success =) buffer.readScalarArray(fKernel, size); SkASSERT(success); } else {
SkTileGrid::SkTileGrid(int xTileCount, int yTileCount, const SkTileGridPicture::TileGridInfo& info, SkTileGridNextDatumFunctionPtr nextDatumFunction) { fXTileCount = xTileCount; fYTileCount = yTileCount; fInfo = info; // Margin is offset by 1 as a provision for AA and // to cancel-out the outset applied by getClipDeviceBounds. fInfo.fMargin.fHeight++; fInfo.fMargin.fWidth++; fTileCount = fXTileCount * fYTileCount; fInsertionCount = 0; fGridBounds = SkIRect::MakeXYWH(0, 0, fInfo.fTileInterval.width() * fXTileCount, fInfo.fTileInterval.height() * fYTileCount); fNextDatumFunction = nextDatumFunction; fTileData = SkNEW_ARRAY(SkTDArray<void *>, fTileCount); }
SkRTree::Branch* SkRTree::insert(Node* root, Branch* branch, uint16_t level) { Branch* toInsert = branch; if (root->fLevel != level) { int childIndex = this->chooseSubtree(root, branch); toInsert = this->insert(root->child(childIndex)->fChild.subtree, branch, level); root->child(childIndex)->fBounds = this->computeBounds( root->child(childIndex)->fChild.subtree); } if (NULL != toInsert) { if (root->fNumChildren == fMaxChildren) { // handle overflow by splitting. TODO: opportunistic reinsertion // decide on a distribution to divide with Node* newSibling = this->allocateNode(root->fLevel); Branch* toDivide = SkNEW_ARRAY(Branch, fMaxChildren + 1); for (int i = 0; i < fMaxChildren; ++i) { toDivide[i] = *root->child(i); } toDivide[fMaxChildren] = *toInsert; int splitIndex = this->distributeChildren(toDivide); // divide up the branches root->fNumChildren = splitIndex; newSibling->fNumChildren = fMaxChildren + 1 - splitIndex; for (int i = 0; i < splitIndex; ++i) { *root->child(i) = toDivide[i]; } for (int i = splitIndex; i < fMaxChildren + 1; ++i) { *newSibling->child(i - splitIndex) = toDivide[i]; } SkDELETE_ARRAY(toDivide); // pass the new sibling branch up to the parent branch->fChild.subtree = newSibling; branch->fBounds = this->computeBounds(newSibling); return branch; } else { *root->child(root->fNumChildren) = *toInsert; ++root->fNumChildren; return NULL; } } return NULL; }
static void check_fill(skiatest::Reporter* r, const SkImageInfo& imageInfo, uint32_t startRow, uint32_t endRow, size_t rowBytes, uint32_t offset, uint32_t colorOrIndex, SkPMColor* colorTable) { // Calculate the total size of the image in bytes. Use the smallest possible size. // The offset value tells us to adjust the pointer from the memory we allocate in order // to test on different memory alignments. If offset is nonzero, we need to increase the // size of the memory we allocate in order to make sure that we have enough. We are // still allocating the smallest possible size. const size_t totalBytes = imageInfo.getSafeSize(rowBytes) + offset; // Create fake image data where every byte has a value of 0 SkAutoTDeleteArray<uint8_t> storage(SkNEW_ARRAY(uint8_t, totalBytes)); memset(storage.get(), 0, totalBytes); // Adjust the pointer in order to test on different memory alignments uint8_t* imageData = storage.get() + offset; uint8_t* imageStart = imageData + rowBytes * startRow; // Fill image with the fill value starting at the indicated row SkSwizzler::Fill(imageStart, imageInfo, rowBytes, endRow - startRow + 1, colorOrIndex, colorTable); // Ensure that the pixels are filled properly // The bots should catch any memory corruption uint8_t* indexPtr = imageData + startRow * rowBytes; uint32_t* colorPtr = (uint32_t*) indexPtr; for (uint32_t y = startRow; y <= endRow; y++) { for (int32_t x = 0; x < imageInfo.width(); x++) { if (kIndex_8_SkColorType == imageInfo.colorType()) { REPORTER_ASSERT(r, kFillIndex == indexPtr[x]); } else { REPORTER_ASSERT(r, kFillColor == colorPtr[x]); } } indexPtr += rowBytes; colorPtr = (uint32_t*) indexPtr; } }
void SkPDFDeviceFlattener::drawPoints(const SkDraw& d, SkCanvas::PointMode mode, size_t count, const SkPoint points[], const SkPaint& paint) { if (!mustFlatten(d)) { INHERITED::drawPoints(d, mode, count, points, paint); return; } SkPaint paintFlatten(paint); flattenPaint(d, &paintFlatten); SkPoint* flattenedPoints = SkNEW_ARRAY(SkPoint, count); d.fMatrix->mapPoints(flattenedPoints, points, count); SkDraw draw(d); SkMatrix identity = SkMatrix::I(); draw.fMatrix = &identity; INHERITED::drawPoints(draw, mode, count, flattenedPoints, paintFlatten); SkDELETE_ARRAY(flattenedPoints); }
GrStrokeInfo TestStrokeInfo(SkRandom* random) { SkStrokeRec::InitStyle style = SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1)); GrStrokeInfo strokeInfo(style); randomize_stroke_rec(&strokeInfo, random); SkPathEffect::DashInfo dashInfo; dashInfo.fCount = random->nextRangeU(1, 50) * 2; SkAutoTDeleteArray<SkScalar> intervals(SkNEW_ARRAY(SkScalar, dashInfo.fCount)); dashInfo.fIntervals = intervals.get(); SkScalar sum = 0; for (int i = 0; i < dashInfo.fCount; i++) { dashInfo.fIntervals[i] = random->nextRangeScalar(SkDoubleToScalar(0.01), SkDoubleToScalar(10.0)); sum += dashInfo.fIntervals[i]; } dashInfo.fPhase = random->nextRangeScalar(0, sum); strokeInfo.setDashInfo(dashInfo); return strokeInfo; }
GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config) { fGpu = gpu; fPixelConfig = config; gpu->ref(); fTexture = NULL; // set up allocated plots size_t bpp = GrBytesPerPixel(fPixelConfig); fPlotArray = SkNEW_ARRAY(GrPlot, (GR_PLOT_WIDTH*GR_PLOT_HEIGHT)); GrPlot* currPlot = fPlotArray; for (int y = GR_PLOT_HEIGHT-1; y >= 0; --y) { for (int x = GR_PLOT_WIDTH-1; x >= 0; --x) { currPlot->fAtlasMgr = this; currPlot->fOffset.set(x, y); currPlot->fBytesPerPixel = bpp; // build LRU list fPlotList.addToHead(currPlot); ++currPlot; } } }
void SubsetSingleBench::onDraw(const int n, SkCanvas* canvas) { // When the color type is kIndex8, we will need to store the color table. If it is // used, it will be initialized by the codec. int colorCount; SkPMColor colors[256]; if (fUseCodec) { for (int count = 0; count < n; count++) { SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(fStream->duplicate())); const SkImageInfo info = codec->getInfo().makeColorType(fColorType); SkAutoTDeleteArray<uint8_t> row(SkNEW_ARRAY(uint8_t, info.minRowBytes())); SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder( info, NULL, colors, &colorCount); SkBitmap bitmap; bitmap.allocPixels(info.makeWH(fSubsetWidth, fSubsetHeight)); scanlineDecoder->skipScanlines(fOffsetTop); uint32_t bpp = info.bytesPerPixel(); for (uint32_t y = 0; y < fSubsetHeight; y++) { scanlineDecoder->getScanlines(row.get(), 1, 0); memcpy(bitmap.getAddr(0, y), row.get() + fOffsetLeft * bpp, fSubsetWidth * bpp); } } } else { for (int count = 0; count < n; count++) { SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); int width, height; decoder->buildTileIndex(fStream->duplicate(), &width, &height); SkBitmap bitmap; SkIRect rect = SkIRect::MakeXYWH(fOffsetLeft, fOffsetTop, fSubsetWidth, fSubsetHeight); decoder->decodeSubset(&bitmap, rect, fColorType); } } }
SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter( const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& kernelOffset, TileMode tileMode, bool convolveAlpha, SkImageFilter* input, const CropRect* cropRect) : INHERITED(1, &input, cropRect), fKernelSize(kernelSize), fGain(gain), fBias(bias), fKernelOffset(kernelOffset), fTileMode(tileMode), fConvolveAlpha(convolveAlpha) { size_t size = (size_t) sk_64_mul(fKernelSize.width(), fKernelSize.height()); fKernel = SkNEW_ARRAY(SkScalar, size); memcpy(fKernel, kernel, size * sizeof(SkScalar)); SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1); SkASSERT(kernelOffset.fX >= 0 && kernelOffset.fX < kernelSize.fWidth); SkASSERT(kernelOffset.fY >= 0 && kernelOffset.fY < kernelSize.fHeight); }
/* * Process the color table for the bmp input */ bool SkBmpRLECodec::createColorTable(int* numColors) { // Allocate memory for color table uint32_t colorBytes = 0; SkPMColor colorTable[256]; if (this->bitsPerPixel() <= 8) { // Inform the caller of the number of colors uint32_t maxColors = 1 << this->bitsPerPixel(); if (NULL != numColors) { // We set the number of colors to maxColors in order to ensure // safe memory accesses. Otherwise, an invalid pixel could // access memory outside of our color table array. *numColors = maxColors; } // Read the color table from the stream colorBytes = fNumColors * fBytesPerColor; SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes)); if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) { SkCodecPrintf("Error: unable to read color table.\n"); return false; } // Fill in the color table uint32_t i = 0; for (; i < fNumColors; i++) { uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor); uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1); uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2); colorTable[i] = SkPackARGB32NoCheck(0xFF, red, green, blue); } // To avoid segmentation faults on bad pixel data, fill the end of the // color table with black. This is the same the behavior as the // chromium decoder. for (; i < maxColors; i++) { colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0); } // Set the color table fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors))); } // Check that we have not read past the pixel array offset if(fOffset < colorBytes) { // This may occur on OS 2.1 and other old versions where the color // table defaults to max size, and the bmp tries to use a smaller // color table. This is invalid, and our decision is to indicate // an error, rather than try to guess the intended size of the // color table. SkCodecPrintf("Error: pixel data offset less than color table size.\n"); return false; } // After reading the color table, skip to the start of the pixel array if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) { SkCodecPrintf("Error: unable to skip to image data.\n"); return false; } // Return true on success return true; }
bool SkPatchUtils::getVertexData(SkPatchUtils::VertexData* data, const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], int lodX, int lodY) { if (lodX < 1 || lodY < 1 || NULL == cubics || NULL == data) { return false; } // check for overflow in multiplication const int64_t lodX64 = (lodX + 1), lodY64 = (lodY + 1), mult64 = lodX64 * lodY64; if (mult64 > SK_MaxS32) { return false; } data->fVertexCount = SkToS32(mult64); // it is recommended to generate draw calls of no more than 65536 indices, so we never generate // more than 60000 indices. To accomplish that we resize the LOD and vertex count if (data->fVertexCount > 10000 || lodX > 200 || lodY > 200) { SkScalar weightX = static_cast<SkScalar>(lodX) / (lodX + lodY); SkScalar weightY = static_cast<SkScalar>(lodY) / (lodX + lodY); // 200 comes from the 100 * 2 which is the max value of vertices because of the limit of // 60000 indices ( sqrt(60000 / 6) that comes from data->fIndexCount = lodX * lodY * 6) lodX = static_cast<int>(weightX * 200); lodY = static_cast<int>(weightY * 200); data->fVertexCount = (lodX + 1) * (lodY + 1); } data->fIndexCount = lodX * lodY * 6; data->fPoints = SkNEW_ARRAY(SkPoint, data->fVertexCount); data->fIndices = SkNEW_ARRAY(uint16_t, data->fIndexCount); // if colors is not null then create array for colors SkPMColor colorsPM[kNumCorners]; if (NULL != colors) { // premultiply colors to avoid color bleeding. for (int i = 0; i < kNumCorners; i++) { colorsPM[i] = SkPreMultiplyColor(colors[i]); } data->fColors = SkNEW_ARRAY(uint32_t, data->fVertexCount); } // if texture coordinates are not null then create array for them if (NULL != texCoords) { data->fTexCoords = SkNEW_ARRAY(SkPoint, data->fVertexCount); } SkPoint pts[kNumPtsCubic]; SkPatchUtils::getBottomCubic(cubics, pts); FwDCubicEvaluator fBottom(pts); SkPatchUtils::getTopCubic(cubics, pts); FwDCubicEvaluator fTop(pts); SkPatchUtils::getLeftCubic(cubics, pts); FwDCubicEvaluator fLeft(pts); SkPatchUtils::getRightCubic(cubics, pts); FwDCubicEvaluator fRight(pts); fBottom.restart(lodX); fTop.restart(lodX); SkScalar u = 0.0f; int stride = lodY + 1; for (int x = 0; x <= lodX; x++) { SkPoint bottom = fBottom.next(), top = fTop.next(); fLeft.restart(lodY); fRight.restart(lodY); SkScalar v = 0.f; for (int y = 0; y <= lodY; y++) { int dataIndex = x * (lodY + 1) + y; SkPoint left = fLeft.next(), right = fRight.next(); SkPoint s0 = SkPoint::Make((1.0f - v) * top.x() + v * bottom.x(), (1.0f - v) * top.y() + v * bottom.y()); SkPoint s1 = SkPoint::Make((1.0f - u) * left.x() + u * right.x(), (1.0f - u) * left.y() + u * right.y()); SkPoint s2 = SkPoint::Make( (1.0f - v) * ((1.0f - u) * fTop.getCtrlPoints()[0].x() + u * fTop.getCtrlPoints()[3].x()) + v * ((1.0f - u) * fBottom.getCtrlPoints()[0].x() + u * fBottom.getCtrlPoints()[3].x()), (1.0f - v) * ((1.0f - u) * fTop.getCtrlPoints()[0].y() + u * fTop.getCtrlPoints()[3].y()) + v * ((1.0f - u) * fBottom.getCtrlPoints()[0].y() + u * fBottom.getCtrlPoints()[3].y())); data->fPoints[dataIndex] = s0 + s1 - s2; if (NULL != colors) { uint8_t a = uint8_t(bilerp(u, v, SkScalar(SkColorGetA(colorsPM[kTopLeft_Corner])), SkScalar(SkColorGetA(colorsPM[kTopRight_Corner])), SkScalar(SkColorGetA(colorsPM[kBottomLeft_Corner])), SkScalar(SkColorGetA(colorsPM[kBottomRight_Corner])))); uint8_t r = uint8_t(bilerp(u, v, SkScalar(SkColorGetR(colorsPM[kTopLeft_Corner])), SkScalar(SkColorGetR(colorsPM[kTopRight_Corner])), SkScalar(SkColorGetR(colorsPM[kBottomLeft_Corner])), SkScalar(SkColorGetR(colorsPM[kBottomRight_Corner])))); uint8_t g = uint8_t(bilerp(u, v, SkScalar(SkColorGetG(colorsPM[kTopLeft_Corner])), SkScalar(SkColorGetG(colorsPM[kTopRight_Corner])), SkScalar(SkColorGetG(colorsPM[kBottomLeft_Corner])), SkScalar(SkColorGetG(colorsPM[kBottomRight_Corner])))); uint8_t b = uint8_t(bilerp(u, v, SkScalar(SkColorGetB(colorsPM[kTopLeft_Corner])), SkScalar(SkColorGetB(colorsPM[kTopRight_Corner])), SkScalar(SkColorGetB(colorsPM[kBottomLeft_Corner])), SkScalar(SkColorGetB(colorsPM[kBottomRight_Corner])))); data->fColors[dataIndex] = SkPackARGB32(a,r,g,b); } if (NULL != texCoords) { data->fTexCoords[dataIndex] = SkPoint::Make( bilerp(u, v, texCoords[kTopLeft_Corner].x(), texCoords[kTopRight_Corner].x(), texCoords[kBottomLeft_Corner].x(), texCoords[kBottomRight_Corner].x()), bilerp(u, v, texCoords[kTopLeft_Corner].y(), texCoords[kTopRight_Corner].y(), texCoords[kBottomLeft_Corner].y(), texCoords[kBottomRight_Corner].y())); } if(x < lodX && y < lodY) { int i = 6 * (x * lodY + y); data->fIndices[i] = x * stride + y; data->fIndices[i + 1] = x * stride + 1 + y; data->fIndices[i + 2] = (x + 1) * stride + 1 + y; data->fIndices[i + 3] = data->fIndices[i]; data->fIndices[i + 4] = data->fIndices[i + 2]; data->fIndices[i + 5] = (x + 1) * stride + y; } v = SkScalarClampMax(v + 1.f / lodY, 1); } u = SkScalarClampMax(u + 1.f / lodX, 1); } return true; }
/* * Assumes IsIco was called and returned true * Creates an Ico decoder * Reads enough of the stream to determine the image format */ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) { // Ensure that we do not leak the input stream SkAutoTDelete<SkStream> inputStream(stream); // Header size constants static const uint32_t kIcoDirectoryBytes = 6; static const uint32_t kIcoDirEntryBytes = 16; // Read the directory header SkAutoTDeleteArray<uint8_t> dirBuffer( SkNEW_ARRAY(uint8_t, kIcoDirectoryBytes)); if (inputStream.get()->read(dirBuffer.get(), kIcoDirectoryBytes) != kIcoDirectoryBytes) { SkCodecPrintf("Error: unable to read ico directory header.\n"); return NULL; } // Process the directory header const uint16_t numImages = get_short(dirBuffer.get(), 4); if (0 == numImages) { SkCodecPrintf("Error: No images embedded in ico.\n"); return NULL; } // Ensure that we can read all of indicated directory entries SkAutoTDeleteArray<uint8_t> entryBuffer( SkNEW_ARRAY(uint8_t, numImages*kIcoDirEntryBytes)); if (inputStream.get()->read(entryBuffer.get(), numImages*kIcoDirEntryBytes) != numImages*kIcoDirEntryBytes) { SkCodecPrintf("Error: unable to read ico directory entries.\n"); return NULL; } // This structure is used to represent the vital information about entries // in the directory header. We will obtain this information for each // directory entry. struct Entry { uint32_t offset; uint32_t size; }; SkAutoTDeleteArray<Entry> directoryEntries(SkNEW_ARRAY(Entry, numImages)); // Iterate over directory entries for (uint32_t i = 0; i < numImages; i++) { // The directory entry contains information such as width, height, // bits per pixel, and number of colors in the color palette. We will // ignore these fields since they are repeated in the header of the // embedded image. In the event of an inconsistency, we would always // defer to the value in the embedded header anyway. // Specifies the size of the embedded image, including the header uint32_t size = get_int(entryBuffer.get(), 8 + i*kIcoDirEntryBytes); // Specifies the offset of the embedded image from the start of file. // It does not indicate the start of the pixel data, but rather the // start of the embedded image header. uint32_t offset = get_int(entryBuffer.get(), 12 + i*kIcoDirEntryBytes); // Save the vital fields directoryEntries.get()[i].offset = offset; directoryEntries.get()[i].size = size; } // It is "customary" that the embedded images will be stored in order of // increasing offset. However, the specification does not indicate that // they must be stored in this order, so we will not trust that this is the // case. Here we sort the embedded images by increasing offset. struct EntryLessThan { bool operator() (Entry a, Entry b) const { return a.offset < b.offset; } }; EntryLessThan lessThan; SkTQSort(directoryEntries.get(), directoryEntries.get() + numImages - 1, lessThan); // Now will construct a candidate codec for each of the embedded images uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes; SkAutoTDelete<SkTArray<SkAutoTDelete<SkCodec>, true>> codecs( SkNEW_ARGS((SkTArray<SkAutoTDelete<SkCodec>, true>), (numImages))); for (uint32_t i = 0; i < numImages; i++) { uint32_t offset = directoryEntries.get()[i].offset; uint32_t size = directoryEntries.get()[i].size; // Ensure that the offset is valid if (offset < bytesRead) { SkCodecPrintf("Warning: invalid ico offset.\n"); continue; } // If we cannot skip, assume we have reached the end of the stream and // stop trying to make codecs if (inputStream.get()->skip(offset - bytesRead) != offset - bytesRead) { SkCodecPrintf("Warning: could not skip to ico offset.\n"); break; } bytesRead = offset; // Create a new stream for the embedded codec SkAutoTUnref<SkData> data( SkData::NewFromStream(inputStream.get(), size)); if (NULL == data.get()) { SkCodecPrintf("Warning: could not create embedded stream.\n"); break; } SkAutoTDelete<SkMemoryStream> embeddedStream(SkNEW_ARGS(SkMemoryStream, (data.get()))); bytesRead += size; // Check if the embedded codec is bmp or png and create the codec const bool isPng = SkPngCodec::IsPng(embeddedStream); SkAssertResult(embeddedStream->rewind()); SkCodec* codec = NULL; if (isPng) { codec = SkPngCodec::NewFromStream(embeddedStream.detach()); } else { codec = SkBmpCodec::NewFromIco(embeddedStream.detach()); } // Save a valid codec if (NULL != codec) { codecs->push_back().reset(codec); } } // Recognize if there are no valid codecs if (0 == codecs->count()) { SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n"); return NULL; } // Use the largest codec as a "suggestion" for image info uint32_t maxSize = 0; uint32_t maxIndex = 0; for (int32_t i = 0; i < codecs->count(); i++) { SkImageInfo info = codecs->operator[](i)->getInfo(); uint32_t size = info.width() * info.height(); if (size > maxSize) { maxSize = size; maxIndex = i; } } SkImageInfo info = codecs->operator[](maxIndex)->getInfo(); // Note that stream is owned by the embedded codec, the ico does not need // direct access to the stream. return SkNEW_ARGS(SkIcoCodec, (info, codecs.detach())); }
/* * Performs the jpeg decode */ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& options, SkPMColor*, int*) { // Rewind the stream if needed if (!this->handleRewind()) { fDecoderMgr->returnFailure("could not rewind stream", kCouldNotRewind); } // Get a pointer to the decompress info since we will use it quite frequently jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); // Set the jump location for libjpeg errors if (setjmp(fDecoderMgr->getJmpBuf())) { return fDecoderMgr->returnFailure("setjmp", kInvalidInput); } // Check if we can decode to the requested destination if (!conversion_possible(dstInfo, this->getInfo())) { return fDecoderMgr->returnFailure("conversion_possible", kInvalidConversion); } // Perform the necessary scaling if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) { fDecoderMgr->returnFailure("cannot scale to requested dims", kInvalidScale); } // Now, given valid output dimensions, we can start the decompress if (!jpeg_start_decompress(dinfo)) { return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); } // Create the swizzler this->initializeSwizzler(dstInfo, dst, dstRowBytes, options); if (NULL == fSwizzler) { return fDecoderMgr->returnFailure("getSwizzler", kUnimplemented); } // This is usually 1, but can also be 2 or 4. // If we wanted to always read one row at a time, we could, but we will save space and time // by using the recommendation from libjpeg. const uint32_t rowsPerDecode = dinfo->rec_outbuf_height; SkASSERT(rowsPerDecode <= 4); // Create a buffer to contain decoded rows (libjpeg requires a 2D array) SkASSERT(0 != fSrcRowBytes); SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, fSrcRowBytes * rowsPerDecode)); JSAMPLE* srcRows[4]; uint8_t* srcPtr = srcBuffer.get(); for (uint8_t i = 0; i < rowsPerDecode; i++) { srcRows[i] = (JSAMPLE*) srcPtr; srcPtr += fSrcRowBytes; } // Ensure that we loop enough times to decode all of the rows // libjpeg will prevent us from reading past the bottom of the image uint32_t dstHeight = dstInfo.height(); for (uint32_t y = 0; y < dstHeight + rowsPerDecode - 1; y += rowsPerDecode) { // Read rows of the image uint32_t rowsDecoded = jpeg_read_scanlines(dinfo, srcRows, rowsPerDecode); // Convert to RGB if necessary if (JCS_CMYK == dinfo->out_color_space) { convert_CMYK_to_RGB(srcRows[0], dstInfo.width() * rowsDecoded); } // Swizzle to output destination for (uint32_t i = 0; i < rowsDecoded; i++) { fSwizzler->next(srcRows[i]); } // If we cannot read enough rows, assume the input is incomplete if (rowsDecoded < rowsPerDecode && y + rowsDecoded < dstHeight) { // Fill the remainder of the image with black. This error handling // behavior is unspecified but SkCodec consistently uses black as // the fill color for opaque images. If the destination is kGray, // the low 8 bits of SK_ColorBLACK will be used. Conveniently, // these are zeros, which is the representation for black in kGray. SkSwizzler::Fill(fSwizzler->getDstRow(), dstInfo, dstRowBytes, dstHeight - y - rowsDecoded, SK_ColorBLACK, NULL); // Prevent libjpeg from failing on incomplete decode dinfo->output_scanline = dstHeight; // Finish the decode and indicate that the input was incomplete. jpeg_finish_decompress(dinfo); return fDecoderMgr->returnFailure("Incomplete image data", kIncompleteInput); } } jpeg_finish_decompress(dinfo); return kSuccess; }
void GrDistanceFieldTextContext::buildDistanceAdjustTable() { // This is used for an approximation of the mask gamma hack, used by raster and bitmap // text. The mask gamma hack is based off of guessing what the blend color is going to // be, and adjusting the mask so that when run through the linear blend will // produce the value closest to the desired result. However, in practice this means // that the 'adjusted' mask is just increasing or decreasing the coverage of // the mask depending on what it is thought it will blit against. For black (on // assumed white) this means that coverages are decreased (on a curve). For white (on // assumed black) this means that coverages are increased (on a a curve). At // middle (perceptual) gray (which could be blit against anything) the coverages // remain the same. // // The idea here is that instead of determining the initial (real) coverage and // then adjusting that coverage, we determine an adjusted coverage directly by // essentially manipulating the geometry (in this case, the distance to the glyph // edge). So for black (on assumed white) this thins a bit; for white (on // assumed black) this fake bolds the geometry a bit. // // The distance adjustment is calculated by determining the actual coverage value which // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the // actual edge. So by subtracting this distance adjustment and computing without the // the coverage adjustment we should get 0.5 coverage at the same point. // // This has several implications: // For non-gray lcd smoothed text, each subpixel essentially is using a // slightly different geometry. // // For black (on assumed white) this may not cover some pixels which were // previously covered; however those pixels would have been only slightly // covered and that slight coverage would have been decreased anyway. Also, some pixels // which were previously fully covered may no longer be fully covered. // // For white (on assumed black) this may cover some pixels which weren't // previously covered at all. int width, height; size_t size; #ifdef SK_GAMMA_CONTRAST SkScalar contrast = SK_GAMMA_CONTRAST; #else SkScalar contrast = 0.5f; #endif SkScalar paintGamma = fDeviceProperties.gamma(); SkScalar deviceGamma = fDeviceProperties.gamma(); size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, &width, &height); SkASSERT(kExpectedDistanceAdjustTableSize == height); fDistanceAdjustTable = SkNEW_ARRAY(SkScalar, height); SkAutoTArray<uint8_t> data((int)size); SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get()); // find the inverse points where we cross 0.5 // binsearch might be better, but we only need to do this once on creation for (int row = 0; row < height; ++row) { uint8_t* rowPtr = data.get() + row*width; for (int col = 0; col < width - 1; ++col) { if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { // compute point where a mask value will give us a result of 0.5 float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]); float borderAlpha = (col + interp) / 255.f; // compute t value for that alpha // this is an approximate inverse for smoothstep() float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f; // compute distance which gives us that t value const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor; fDistanceAdjustTable[row] = d; break; } } } }