static unsigned color_dist16(uint16_t a, uint16_t b) { unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b)); unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b)); unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b)); return SkMax32(dr, SkMax32(dg, db)); }
static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy) { dx = SkAbs32(dx); dy = SkAbs32(dy); // return max + min/2 if (dx > dy) dx += dy >> 1; else
/* return non-zero if the path is too big, and should be shrunk to avoid overflows during intermediate calculations. Note that we compute the bounds for this. If we had a custom callback/walker for paths, we could perhaps go faster by using that, and just perform the abs | in that routine */ static int needs_to_shrink(const SkPath& path) { const SkRect& r = path.getBounds(); SkFixed mask = SkAbs32(r.fLeft); mask |= SkAbs32(r.fTop); mask |= SkAbs32(r.fRight); mask |= SkAbs32(r.fBottom); // we need the top 3 bits clear (after abs) to avoid overflow return mask >> 29; }
// returns 0..255 static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) { SkASSERT(r <= 0xFF); SkASSERT(g <= 0xFF); SkASSERT(b <= 0xFF); unsigned dr = SkAbs32(SkGetPackedR32(c) - r); unsigned dg = SkAbs32(SkGetPackedG32(c) - g); unsigned db = SkAbs32(SkGetPackedB32(c) - b); return SkMax32(dr, SkMax32(dg, db)); }
// returns 0..31 static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b) { SkASSERT(r <= SK_R16_MASK); SkASSERT(g <= SK_G16_MASK); SkASSERT(b <= SK_B16_MASK); unsigned dr = SkAbs32(SkGetPackedR16(c) - r); unsigned dg = SkAbs32(SkGetPackedG16(c) - g) >> (SK_G16_BITS - SK_R16_BITS); unsigned db = SkAbs32(SkGetPackedB16(c) - b); return SkMax32(dr, SkMax32(dg, db)); }
// returns 0..15 static unsigned color_dist4444(uint16_t c, unsigned r, unsigned g, unsigned b) { SkASSERT(r <= 0xF); SkASSERT(g <= 0xF); SkASSERT(b <= 0xF); unsigned dr = SkAbs32(SkGetPackedR4444(c) - r); unsigned dg = SkAbs32(SkGetPackedG4444(c) - g); unsigned db = SkAbs32(SkGetPackedB4444(c) - b); return SkMax32(dr, SkMax32(dg, db)); }
static inline SkFixed quickSkFDot6Div(SkFDot6 a, SkFDot6 b) { if (SkAbs32(b) < kInverseTableSize) { SkASSERT((int64_t)a * QuickFDot6Inverse::Lookup(b) <= SK_MaxS32); SkFixed ourAnswer = (a * QuickFDot6Inverse::Lookup(b)) >> 6; #ifdef SK_DEBUG SkFixed directAnswer = SkFDot6Div(a, b); SkASSERT( (directAnswer == 0 && ourAnswer == 0) || SkFixedDiv(SkAbs32(directAnswer - ourAnswer), SkAbs32(directAnswer)) <= 1 << 10 ); #endif return ourAnswer; } else {
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, jint offset, jint stride, jint width, jint height, jint configHandle, jboolean isMutable) { SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle); if (NULL != jColors) { size_t n = env->GetArrayLength(jColors); if (n < SkAbs32(stride) * (size_t)height) { doThrowAIOOBE(env); return NULL; } } // ARGB_4444 is a deprecated format, convert automatically to 8888 if (colorType == kARGB_4444_SkColorType) { colorType = kN32_SkColorType; } SkBitmap bitmap; bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType)); jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); if (NULL == buff) { return NULL; } if (jColors != NULL) { GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap); } return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, getPremulBitmapCreateFlags(isMutable), NULL, NULL); }
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, jint offset, jint stride, jint width, jint height, jint configHandle, jboolean isMutable) { SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle); if (NULL != jColors) { size_t n = env->GetArrayLength(jColors); if (n < SkAbs32(stride) * (size_t)height) { doThrowAIOOBE(env); return NULL; } } // ARGB_4444 is a deprecated format, convert automatically to 8888 if (config == SkBitmap::kARGB_4444_Config) { config = SkBitmap::kARGB_8888_Config; } SkBitmap bitmap; bitmap.setConfig(config, width, height); jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); if (NULL == buff) { return NULL; } if (jColors != NULL) { GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap, true); } return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, getPremulBitmapCreateFlags(isMutable), NULL, NULL); }
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, int offset, int stride, int width, int height, SkBitmap::Config config, jboolean isMutable) { if (NULL != jColors) { size_t n = env->GetArrayLength(jColors); if (n < SkAbs32(stride) * (size_t)height) { doThrowAIOOBE(env); return NULL; } } SkBitmap bitmap; bitmap.setConfig(config, width, height); jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL); if (NULL == buff) { return NULL; } if (jColors != NULL) { GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap); } return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL); }
static int similarBits(const SkBitmap& gr, const SkBitmap& sk) { const int kRowCount = 3; const int kThreshold = 3; int width = SkTMin(gr.width(), sk.width()); if (width < kRowCount) { return true; } int height = SkTMin(gr.height(), sk.height()); if (height < kRowCount) { return true; } int errorTotal = 0; SkTArray<int, true> errorRows; errorRows.push_back_n(width * kRowCount); SkAutoLockPixels autoGr(gr); SkAutoLockPixels autoSk(sk); for (int y = 0; y < height; ++y) { SkPMColor* grRow = gr.getAddr32(0, y); SkPMColor* skRow = sk.getAddr32(0, y); int* base = &errorRows[0]; int* cOut = &errorRows[y % kRowCount]; for (int x = 0; x < width; ++x) { SkPMColor grColor = grRow[x]; SkPMColor skColor = skRow[x]; int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor); int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor); int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor); int error = cOut[x] = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db))); if (error < kThreshold || x < 2) { continue; } if (base[x - 2] < kThreshold || base[width + x - 2] < kThreshold || base[width * 2 + x - 2] < kThreshold || base[x - 1] < kThreshold || base[width + x - 1] < kThreshold || base[width * 2 + x - 1] < kThreshold || base[x] < kThreshold || base[width + x] < kThreshold || base[width * 2 + x] < kThreshold) { continue; } errorTotal += error; } } return errorTotal; }
uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc, int blurred_width, int sharp_width) { int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge? int ox = dx >> 1; if (ox < 0) { ox = 0; } return profile[ox]; }
static inline unsigned int profile_lookup( unsigned int *profile, int loc, int blurred_width, int sharp_width ) { int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge? int ox = dx >> 1; if (ox < 0) { ox = 0; } return profile[ox]; }
sk_sp<SkImage> SkReadBuffer::readImage() { if (fInflator) { SkImage* img = fInflator->getImage(this->read32()); return img ? sk_ref_sp(img) : nullptr; } int width = this->read32(); int height = this->read32(); if (width <= 0 || height <= 0) { // SkImage never has a zero dimension this->validate(false); return nullptr; } int32_t size = this->read32(); if (size == SK_NaN32) { // 0x80000000 is never valid, since it cannot be passed to abs(). this->validate(false); return nullptr; } if (size == 0) { // The image could not be encoded at serialization time - return an empty placeholder. return MakeEmptyImage(width, height); } // we used to negate the size for "custom" encoded images -- ignore that signal (Dec-2017) size = SkAbs32(size); if (size == 1) { // legacy check (we stopped writing this for "raw" images Nov-2017) this->validate(false); return nullptr; } sk_sp<SkData> data = SkData::MakeUninitialized(size); if (!this->readPad32(data->writable_data(), size)) { this->validate(false); return nullptr; } if (this->isVersionLT(kDontNegateImageSize_Version)) { (void)this->read32(); // originX (void)this->read32(); // originY } sk_sp<SkImage> image; if (fProcs.fImageProc) { image = fProcs.fImageProc(data->data(), data->size(), fProcs.fImageCtx); } if (!image) { image = SkImage::MakeFromEncoded(std::move(data)); } // Question: are we correct to return an "empty" image instead of nullptr, if the decoder // failed for some reason? return image ? image : MakeEmptyImage(width, height); }
SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { int storedCount = buffer.readS32(); this->initAlloc(SkAbs32(storedCount), storedCount < 0); for (int i = 0; i < fCount; ++i) { fFilters[i] = (SkImageFilter*)buffer.readFlattenable(); } if (fModes) { SkASSERT(storedCount < 0); buffer.read(fModes, fCount * sizeof(fModes[0])); } else { SkASSERT(storedCount >= 0); } }
SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { int storedCount = buffer.readInt(); this->initAlloc(SkAbs32(storedCount), storedCount < 0); for (int i = 0; i < fCount; ++i) { fFilters[i] = buffer.readFlattenableT<SkImageFilter>(); } if (fModes) { SkASSERT(storedCount < 0); SkASSERT(buffer.getArrayCount() == fCount * sizeof(fModes[0])); buffer.readByteArray(fModes); } else { SkASSERT(storedCount >= 0); } }
void SkGraphics::Init() { SkGlobals::Init(); #ifdef BUILD_EMBOSS_TABLE SkEmbossMask_BuildTable(); #endif #ifdef BUILD_RADIALGRADIENT_TABLE SkRadialGradient_BuildTable(); #endif #ifdef SK_DEBUGx int i; static const struct { const char* fTypeName; size_t fSizeOf; } gTypeSize[] = { typesizeline(char), typesizeline(short), typesizeline(int), typesizeline(long), typesizeline(size_t), typesizeline(void*), typesizeline(S8CPU), typesizeline(U8CPU), typesizeline(S16CPU), typesizeline(U16CPU), typesizeline(SkPoint), typesizeline(SkRect), typesizeline(SkMatrix), typesizeline(SkPath), typesizeline(SkGlyph), typesizeline(SkRefCnt), typesizeline(SkPaint), typesizeline(SkCanvas), typesizeline(SkBlitter), typesizeline(SkShader), typesizeline(SkXfermode), typesizeline(SkPathEffect) }; #ifdef SK_CPU_BENDIAN SkDebugf("SkGraphics: big-endian\n"); #else SkDebugf("SkGraphics: little-endian\n"); #endif { char test = 0xFF; int itest = test; // promote to int, see if it sign-extended if (itest < 0) SkDebugf("SkGraphics: char is signed\n"); else SkDebugf("SkGraphics: char is unsigned\n"); } for (i = 0; i < (int)SK_ARRAY_COUNT(gTypeSize); i++) { SkDebugf("SkGraphics: sizeof(%s) = %d\n", gTypeSize[i].fTypeName, gTypeSize[i].fSizeOf); } #endif if (false) // test asm fixmul { int j; SkMSec now = SkTime::GetMSecs(); for (j = 0; j < BIG_LOOP_COUNT; j++) { (void)SkFixedMul_portable(0x8000, 0x150000); } SkMSec now2 = SkTime::GetMSecs(); printf("-------- SkFixedMul_portable = %d\n", now2 - now); for (j = 0; j < BIG_LOOP_COUNT; j++) { (void)SkFixedMul(0x8000, 0x150000); } printf("-------- SkFixedMul = %d\n", SkTime::GetMSecs() - now2); SkRandom rand; for (j = 0; j < 10000; j++) { SkFixed a = rand.nextS() >> 8; SkFixed b = rand.nextS() >> 8; SkFixed c1 = SkFixedMul_portable(a, b); SkFixed c2 = SkFixedMul(a, b); if (SkAbs32(c1 - c2) > 1) printf("------ FixMul disagreement: (%x %x) slow=%x fast=%x\n", a, b, c1, c2); } }
void PlatformGraphicsContextSkia::drawLine(const IntPoint& point1, const IntPoint& point2) { StrokeStyle style = m_state->strokeStyle; if (style == NoStroke) return; SkPaint paint; SkCanvas* canvas = mCanvas; const int idx = SkAbs32(point2.x() - point1.x()); const int idy = SkAbs32(point2.y() - point1.y()); // Special-case horizontal and vertical lines that are really just dots if (setupPaintStroke(&paint, 0, !idy) && (!idx || !idy)) { const SkScalar diameter = paint.getStrokeWidth(); const SkScalar radius = SkScalarHalf(diameter); SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x())); SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y())); SkScalar dx, dy; int count; SkRect bounds; if (!idy) { // Horizontal bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius); x += radius; dx = diameter * 2; dy = 0; count = idx; } else { // Vertical bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy)); y += radius; dx = 0; dy = diameter * 2; count = idy; } // The actual count is the number of ONs we hit alternating // ON(diameter), OFF(diameter), ... { SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter); // Now compute the number of cells (ON and OFF) count = SkScalarRound(width); // Now compute the number of ONs count = (count + 1) >> 1; } SkAutoMalloc storage(count * sizeof(SkPoint)); SkPoint* verts = (SkPoint*)storage.get(); // Now build the array of vertices to past to drawPoints for (int i = 0; i < count; i++) { verts[i].set(x, y); x += dx; y += dy; } paint.setStyle(SkPaint::kFill_Style); paint.setPathEffect(0); // Clipping to bounds is not required for correctness, but it does // allow us to reject the entire array of points if we are completely // offscreen. This is common in a webpage for android, where most of // the content is clipped out. If drawPoints took an (optional) bounds // parameter, that might even be better, as we would *just* use it for // culling, and not both wacking the canvas' save/restore stack. canvas->save(SkCanvas::kClip_SaveFlag); canvas->clipRect(bounds); canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint); canvas->restore(); } else {
/** * Test decoding an image in premultiplied mode and unpremultiplied mode and compare * them. */ static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) { // Decode a resource: SkBitmap bm8888; SkBitmap bm8888Unpremul; SkFILEStream stream(filename.c_str()); SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream); if (skip_image_format(format)) { return; } SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream)); if (NULL == decoder.get()) { SkDebugf("couldn't decode %s\n", filename.c_str()); return; } bool success = decoder->decode(&stream, &bm8888, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode); if (!success) { return; } success = stream.rewind(); REPORTER_ASSERT(reporter, success); if (!success) { return; } decoder->setRequireUnpremultipliedColors(true); success = decoder->decode(&stream, &bm8888Unpremul, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode); if (!success) { return; } bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width() && bm8888.height() == bm8888Unpremul.height(); REPORTER_ASSERT(reporter, dimensionsMatch); if (!dimensionsMatch) { return; } // Only do the comparison if the two bitmaps are both 8888. if (bm8888.config() != SkBitmap::kARGB_8888_Config || bm8888Unpremul.config() != SkBitmap::kARGB_8888_Config) { return; } // Now compare the two bitmaps. for (int i = 0; i < bm8888.width(); ++i) { for (int j = 0; j < bm8888.height(); ++j) { // "c0" is the color of the premultiplied bitmap at (i, j). const SkPMColor c0 = *bm8888.getAddr32(i, j); // "c1" is the result of premultiplying the color of the unpremultiplied // bitmap at (i, j). const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j)); // Compute the difference for each component. int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1)); int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1)); int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1)); int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1)); // Alpha component must be exactly the same. REPORTER_ASSERT(reporter, 0 == da); // Color components may not match exactly due to rounding error. REPORTER_ASSERT(reporter, dr <= 1); REPORTER_ASSERT(reporter, dg <= 1); REPORTER_ASSERT(reporter, db <= 1); } } }
// max + 0.5 min has error [0.0, 0.12] // max + 0.375 min has error [-.03, 0.07] // 0.96043387 max + 0.397824735 min has error [-.06, +.05] // For determining the maximum possible number of points to use in // drawing a quadratic, we want to err on the high side. static inline int cheap_distance(SkScalar dx, SkScalar dy) { int idx = SkAbs32(SkScalarRoundToInt(dx)); int idy = SkAbs32(SkScalarRoundToInt(dy)); if (idx > idy) { idx += idy >> 1; } else {
inline static SkFixed Lookup(SkFDot6 x) { SkASSERT(SkAbs32(x) < kInverseTableSize); return table[x]; }