SkFlattenable* SkMatrixConvolutionImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); SkISize kernelSize; kernelSize.fWidth = buffer.readInt(); kernelSize.fHeight = buffer.readInt(); const int count = buffer.getArrayCount(); const int64_t kernelArea = sk_64_mul(kernelSize.width(), kernelSize.height()); if (!buffer.validate(kernelArea == count)) { return nullptr; } SkAutoSTArray<16, SkScalar> kernel(count); if (!buffer.readScalarArray(kernel.get(), count)) { return nullptr; } SkScalar gain = buffer.readScalar(); SkScalar bias = buffer.readScalar(); SkIPoint kernelOffset; kernelOffset.fX = buffer.readInt(); kernelOffset.fY = buffer.readInt(); TileMode tileMode = (TileMode)buffer.readInt(); bool convolveAlpha = buffer.readBool(); return Create(kernelSize, kernel.get(), gain, bias, kernelOffset, tileMode, convolveAlpha, common.getInput(0), &common.cropRect()); }
/** returns the product if it is positive and fits in 31 bits. Otherwise this returns 0. */ static int32_t safeMul32(int32_t a, int32_t b) { int64_t size = sk_64_mul(a, b); if (size > 0 && sk_64_isS32(size)) { return sk_64_asS32(size); } return 0; }
size_t SkMipMap::AllocLevelsSize(int levelCount, size_t pixelSize) { if (levelCount < 0) { return 0; } int64_t size = sk_64_mul(levelCount + 1, sizeof(Level)) + pixelSize; if (!SkTFitsIn<int32_t>(size)) { return 0; } return SkTo<int32_t>(size); }
bool SkRgnBuilder::init(int maxHeight, int maxTransitions, bool pathIsInverse) { if ((maxHeight | maxTransitions) < 0) { return false; } if (pathIsInverse) { // allow for additional X transitions to "invert" each scanline // [ L' ... normal transitions ... R' ] // maxTransitions += 2; } // compute the count with +1 and +3 slop for the working buffer int64_t count = sk_64_mul(maxHeight + 1, 3 + maxTransitions); if (pathIsInverse) { // allow for two "empty" rows for the top and bottom // [ Y, 1, L, R, S] == 5 (*2 for top and bottom) count += 10; } if (count < 0 || !sk_64_isS32(count)) { return false; } fStorageCount = sk_64_asS32(count); int64_t size = sk_64_mul(fStorageCount, sizeof(SkRegion::RunType)); if (size < 0 || !sk_64_isS32(size)) { return false; } fStorage = (SkRegion::RunType*)sk_malloc_flags(sk_64_asS32(size), 0); if (NULL == fStorage) { return false; } fCurrScanline = NULL; // signal empty collection fPrevScanline = NULL; // signal first scanline return true; }
bool SkValidatingReadBuffer::readArray(void* value, size_t size, size_t elementSize) { const uint32_t count = this->getArrayCount(); this->validate(size == count); (void)this->skip(sizeof(uint32_t)); // Skip array count const uint64_t byteLength64 = sk_64_mul(count, elementSize); const size_t byteLength = count * elementSize; this->validate(byteLength == byteLength64); const void* ptr = this->skip(SkAlign4(byteLength)); if (!fError) { memcpy(value, ptr, byteLength); return true; } return false; }
bool SkSurface_Raster::Valid(const SkImageInfo& info, size_t rowBytes) { if (info.isEmpty()) { return false; } static const size_t kMaxTotalSize = SK_MaxS32; int shift = 0; switch (info.colorType()) { case kAlpha_8_SkColorType: shift = 0; break; case kRGB_565_SkColorType: shift = 1; break; case kN32_SkColorType: shift = 2; break; case kRGBA_F16_SkColorType: shift = 3; break; default: return false; } if (kIgnoreRowBytesValue == rowBytes) { return true; } uint64_t minRB = (uint64_t)info.width() << shift; if (minRB > rowBytes) { return false; } size_t alignedRowBytes = rowBytes >> shift << shift; if (alignedRowBytes != rowBytes) { return false; } uint64_t size = sk_64_mul(info.height(), rowBytes); if (size > kMaxTotalSize) { return false; } return true; }
SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& kernelOffset, TileMode tileMode, bool convolveAlpha, sk_sp<SkImageFilter> input, const CropRect* cropRect) : INHERITED(&input, 1, 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 = new 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); }
bool BGRAConvolve2D(const unsigned char* sourceData, int sourceByteRowStride, bool sourceHasAlpha, const SkConvolutionFilter1D& filterX, const SkConvolutionFilter1D& filterY, int outputByteRowStride, unsigned char* output) { int maxYFilterSize = filterY.maxFilter(); // The next row in the input that we will generate a horizontally // convolved row for. If the filter doesn't start at the beginning of the // image (this is the case when we are only resizing a subset), then we // don't want to generate any output rows before that. Compute the starting // row for convolution as the first pixel for the first vertical filter. int filterOffset, filterLength; const SkConvolutionFilter1D::ConvolutionFixed* filterValues = filterY.FilterForValue(0, &filterOffset, &filterLength); int nextXRow = filterOffset; // We loop over each row in the input doing a horizontal convolution. This // will result in a horizontally convolved image. We write the results into // a circular buffer of convolved rows and do vertical convolution as rows // are available. This prevents us from having to store the entire // intermediate image and helps cache coherency. // We will need four extra rows to allow horizontal convolution could be done // simultaneously. We also pad each row in row buffer to be aligned-up to // 32 bytes. // TODO(jiesun): We do not use aligned load from row buffer in vertical // convolution pass yet. Somehow Windows does not like it. int rowBufferWidth = (filterX.numValues() + 31) & ~0x1F; int rowBufferHeight = maxYFilterSize + (SkOpts::convolve_4_rows_horizontally != nullptr ? 4 : 0); // check for too-big allocation requests : crbug.com/528628 { int64_t size = sk_64_mul(rowBufferWidth, rowBufferHeight); // need some limit, to avoid over-committing success from malloc, but then // crashing when we try to actually use the memory. // 100meg seems big enough to allow "normal" zoom factors and image sizes through // while avoiding the crash seen by the bug (crbug.com/528628) if (size > 100 * 1024 * 1024) { // SkDebugf("BGRAConvolve2D: tmp allocation [%lld] too big\n", size); return false; } } CircularRowBuffer rowBuffer(rowBufferWidth, rowBufferHeight, filterOffset); // Loop over every possible output row, processing just enough horizontal // convolutions to run each subsequent vertical convolution. SkASSERT(outputByteRowStride >= filterX.numValues() * 4); int numOutputRows = filterY.numValues(); // We need to check which is the last line to convolve before we advance 4 // lines in one iteration. int lastFilterOffset, lastFilterLength; filterY.FilterForValue(numOutputRows - 1, &lastFilterOffset, &lastFilterLength); for (int outY = 0; outY < numOutputRows; outY++) { filterValues = filterY.FilterForValue(outY, &filterOffset, &filterLength); // Generate output rows until we have enough to run the current filter. while (nextXRow < filterOffset + filterLength) { if (SkOpts::convolve_4_rows_horizontally != nullptr && nextXRow + 3 < lastFilterOffset + lastFilterLength) { const unsigned char* src[4]; unsigned char* outRow[4]; for (int i = 0; i < 4; ++i) { src[i] = &sourceData[(uint64_t)(nextXRow + i) * sourceByteRowStride]; outRow[i] = rowBuffer.advanceRow(); } SkOpts::convolve_4_rows_horizontally(src, filterX, outRow, 4*rowBufferWidth); nextXRow += 4; } else { SkOpts::convolve_horizontally( &sourceData[(uint64_t)nextXRow * sourceByteRowStride], filterX, rowBuffer.advanceRow(), sourceHasAlpha); nextXRow++; } } // Compute where in the output image this row of final data will go. unsigned char* curOutputRow = &output[(uint64_t)outY * outputByteRowStride]; // Get the list of rows that the circular buffer has, in order. int firstRowInCircularBuffer; unsigned char* const* rowsToConvolve = rowBuffer.GetRowAddresses(&firstRowInCircularBuffer); // Now compute the start of the subset of those rows that the filter needs. unsigned char* const* firstRowForFilter = &rowsToConvolve[filterOffset - firstRowInCircularBuffer]; SkOpts::convolve_vertically(filterValues, filterLength, firstRowForFilter, filterX.numValues(), curOutputRow, sourceHasAlpha); } return true; }