/** * Extract either the color or image data from a SkBitmap into a SkStream. * @param bitmap Bitmap to extract data from. * @param srcRect Region in the bitmap to extract. * @param extractAlpha Set to true to extract the alpha data or false to * extract the color data. * @param isTransparent Pointer to a bool to output whether the alpha is * completely transparent. May be NULL. Only valid when * extractAlpha == true. * @return Unencoded image data, or NULL if either data was not * available or alpha data was requested but the image was * entirely transparent or opaque. */ static SkStream* extract_image_data(const SkBitmap& bitmap, const SkIRect& srcRect, bool extractAlpha, bool* isTransparent) { SkColorType colorType = bitmap.colorType(); if (extractAlpha && (kIndex_8_SkColorType == colorType || kRGB_565_SkColorType == colorType)) { if (isTransparent != NULL) { *isTransparent = false; } return NULL; } SkAutoLockPixels lock(bitmap); if (NULL == bitmap.getPixels()) { return NULL; } bool isOpaque = true; bool transparent = extractAlpha; SkAutoTDelete<SkStream> stream; switch (colorType) { case kIndex_8_SkColorType: if (!extractAlpha) { stream.reset(extract_index8_image(bitmap, srcRect)); } break; case kARGB_4444_SkColorType: stream.reset(extract_argb4444_data(bitmap, srcRect, extractAlpha, &isOpaque, &transparent)); break; case kRGB_565_SkColorType: if (!extractAlpha) { stream.reset(extract_rgb565_image(bitmap, srcRect)); } break; case kN32_SkColorType: stream.reset(extract_argb8888_data(bitmap, srcRect, extractAlpha, &isOpaque, &transparent)); break; case kAlpha_8_SkColorType: if (!extractAlpha) { stream.reset(create_black_image()); } else { stream.reset(extract_a8_alpha(bitmap, srcRect, &isOpaque, &transparent)); } break; default: SkASSERT(false); } if (isTransparent != NULL) { *isTransparent = transparent; } if (extractAlpha && (transparent || isOpaque)) { return NULL; } return stream.detach(); }
SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( SkStreamRewindable* stream, Strategy strategy) { SkAutoTDelete<SkStreamRewindable> streamDeleter(stream); switch (strategy) { case kCanvas_Strategy: { SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(streamDeleter.detach())); if (nullptr == codec) { SkCodecPrintf("Error: Failed to create decoder.\n"); return nullptr; } SkEncodedFormat format = codec->getEncodedFormat(); switch (format) { case SkEncodedFormat::kJPEG_SkEncodedFormat: case SkEncodedFormat::kPNG_SkEncodedFormat: break; default: // FIXME: Support webp using a special case. Webp does not support // scanline decoding. return nullptr; } // If the image is a jpeg or a png, the scanline ordering should always be // kTopDown or kNone. It is relevant to check because this implementation // only supports these two scanline orderings. SkASSERT(SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder() || SkCodec::kNone_SkScanlineOrder == codec->getScanlineOrder()); return new SkBitmapRegionCanvas(codec.detach()); } case kAndroidCodec_Strategy: { SkAutoTDelete<SkAndroidCodec> codec = SkAndroidCodec::NewFromStream(streamDeleter.detach()); if (nullptr == codec) { SkCodecPrintf("Error: Failed to create codec.\n"); return NULL; } SkEncodedFormat format = codec->getEncodedFormat(); switch (format) { case SkEncodedFormat::kJPEG_SkEncodedFormat: case SkEncodedFormat::kPNG_SkEncodedFormat: case SkEncodedFormat::kWEBP_SkEncodedFormat: break; default: return nullptr; } return new SkBitmapRegionCodec(codec.detach()); } default: SkASSERT(false); return nullptr; } }
SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( SkStreamRewindable* stream, Strategy strategy) { SkAutoTDelete<SkStreamRewindable> streamDeleter(stream); switch (strategy) { case kCanvas_Strategy: { SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(streamDeleter.detach())); if (nullptr == codec) { SkCodecPrintf("Error: Failed to create decoder.\n"); return nullptr; } if (SkEncodedFormat::kWEBP_SkEncodedFormat == codec->getEncodedFormat()) { // FIXME: Support webp using a special case. Webp does not support // scanline decoding. return nullptr; } switch (codec->getScanlineOrder()) { case SkCodec::kTopDown_SkScanlineOrder: case SkCodec::kNone_SkScanlineOrder: break; default: SkCodecPrintf("Error: Scanline ordering not supported.\n"); return nullptr; } return new SkBitmapRegionCanvas(codec.detach()); } case kAndroidCodec_Strategy: { SkAutoTDelete<SkAndroidCodec> codec = SkAndroidCodec::NewFromStream(streamDeleter.detach()); if (NULL == codec) { SkCodecPrintf("Error: Failed to create codec.\n"); return NULL; } return new SkBitmapRegionCodec(codec.detach()); } default: SkASSERT(false); return nullptr; } }
/* * nine patch not supported * * purgeable not supported * reportSizeToVM not supported */ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint start_x, jint start_y, jint width, jint height, jobject options) { SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); jobject tileBitmap = NULL; SkImageDecoder *decoder = brd->getDecoder(); int sampleSize = 1; SkColorType prefColorType = kUnknown_SkColorType; bool doDither = true; bool preferQualityOverSpeed = false; bool requireUnpremultiplied = false; if (NULL != options) { sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); // initialize these, in case we fail later on env->SetIntField(options, gOptions_widthFieldID, -1); env->SetIntField(options, gOptions_heightFieldID, -1); env->SetObjectField(options, gOptions_mimeFieldID, 0); jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); doDither = env->GetBooleanField(options, gOptions_ditherFieldID); preferQualityOverSpeed = env->GetBooleanField(options, gOptions_preferQualityOverSpeedFieldID); // Get the bitmap for re-use if it exists. tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); } decoder->setDitherImage(doDither); decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); decoder->setRequireUnpremultipliedColors(requireUnpremultiplied); AutoDecoderCancel adc(options, decoder); // To fix the race condition in case "requestCancelDecode" // happens earlier than AutoDecoderCancel object is added // to the gAutoDecoderCancelMutex linked list. if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { return nullObjectReturn("gOptions_mCancelID");; } SkIRect region; region.fLeft = start_x; region.fTop = start_y; region.fRight = start_x + width; region.fBottom = start_y + height; SkBitmap* bitmap = NULL; SkAutoTDelete<SkBitmap> adb; if (tileBitmap != NULL) { // Re-use bitmap. bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap); } if (bitmap == NULL) { bitmap = new SkBitmap; adb.reset(bitmap); } if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) { return nullObjectReturn("decoder->decodeRegion returned false"); } // update options (if any) if (NULL != options) { env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); // TODO: set the mimeType field with the data from the codec. // but how to reuse a set of strings, rather than allocating new one // each time? env->SetObjectField(options, gOptions_mimeFieldID, getMimeTypeString(env, decoder->getFormat())); } if (tileBitmap != NULL) { return tileBitmap; } // detach bitmap from its autodeleter, since we want to own it now adb.detach(); JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator(); jbyteArray buff = allocator->getStorageObjAndReset(); int bitmapCreateFlags = 0; if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1); }
SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData( FontHandle fontHandle, int num_glyphs, const uint32_t* subsetGlyphIDs, uint32_t subsetGlyphIDsLength, bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data)) { // Assuming that on average, the ASCII representation of an advance plus // a space is 8 characters and the ASCII representation of a glyph id is 3 // characters, then the following cut offs for using different range types // apply: // The cost of stopping and starting the range is 7 characers // a. Removing 4 0's or don't care's is a win // The cost of stopping and starting the range plus a run is 22 // characters // b. Removing 3 repeating advances is a win // c. Removing 2 repeating advances and 3 don't cares is a win // When not currently in a range the cost of a run over a range is 16 // characaters, so: // d. Removing a leading 0/don't cares is a win because it is omitted // e. Removing 2 repeating advances is a win SkAutoTDelete<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result; SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange; SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* prevRange = NULL; Data lastAdvance = kInvalidAdvance; int repeatedAdvances = 0; int wildCardsInRun = 0; int trailingWildCards = 0; uint32_t subsetIndex = 0; // Limit the loop count to glyph id ranges provided. int firstIndex = 0; int lastIndex = num_glyphs; if (subsetGlyphIDs) { firstIndex = static_cast<int>(subsetGlyphIDs[0]); lastIndex = static_cast<int>(subsetGlyphIDs[subsetGlyphIDsLength - 1]) + 1; } curRange = appendRange(&result, firstIndex); for (int gId = firstIndex; gId <= lastIndex; gId++) { Data advance = kInvalidAdvance; if (gId < lastIndex) { // Get glyph id only when subset is NULL, or the id is in subset. if (!subsetGlyphIDs || (subsetIndex < subsetGlyphIDsLength && static_cast<uint32_t>(gId) == subsetGlyphIDs[subsetIndex])) { SkAssertResult(getAdvance(fontHandle, gId, &advance)); ++subsetIndex; } else { advance = kDontCareAdvance; } } if (advance == lastAdvance) { repeatedAdvances++; trailingWildCards = 0; } else if (advance == kDontCareAdvance) { wildCardsInRun++; trailingWildCards++; } else if (curRange->fAdvance.count() == repeatedAdvances + 1 + wildCardsInRun) { // All in run. if (lastAdvance == 0) { resetRange(curRange, gId); trailingWildCards = 0; } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) { finishRange(curRange, gId - 1, SkAdvancedTypefaceMetrics::WidthRange::kRun); prevRange = curRange; curRange = appendRange(&curRange->fNext, gId); trailingWildCards = 0; } repeatedAdvances = 0; wildCardsInRun = trailingWildCards; trailingWildCards = 0; } else { if (lastAdvance == 0 && repeatedAdvances + 1 + wildCardsInRun >= 4) { finishRange(curRange, gId - repeatedAdvances - wildCardsInRun - 2, SkAdvancedTypefaceMetrics::WidthRange::kRange); prevRange = curRange; curRange = appendRange(&curRange->fNext, gId); trailingWildCards = 0; } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) { finishRange(curRange, gId - trailingWildCards - 1, SkAdvancedTypefaceMetrics::WidthRange::kRange); prevRange = curRange; curRange = appendRange(&curRange->fNext, gId); trailingWildCards = 0; } else if (lastAdvance != 0 && (repeatedAdvances + 1 >= 3 || (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) { finishRange(curRange, gId - repeatedAdvances - wildCardsInRun - 2, SkAdvancedTypefaceMetrics::WidthRange::kRange); curRange = appendRange(&curRange->fNext, gId - repeatedAdvances - wildCardsInRun - 1); curRange->fAdvance.append(1, &lastAdvance); finishRange(curRange, gId - 1, SkAdvancedTypefaceMetrics::WidthRange::kRun); prevRange = curRange; curRange = appendRange(&curRange->fNext, gId); trailingWildCards = 0; } repeatedAdvances = 0; wildCardsInRun = trailingWildCards; trailingWildCards = 0; } curRange->fAdvance.append(1, &advance); if (advance != kDontCareAdvance) { lastAdvance = advance; } } if (curRange->fStartId == lastIndex) { SkASSERT(prevRange); SkASSERT(prevRange->fNext->fStartId == lastIndex); prevRange->fNext.free(); } else { finishRange(curRange, lastIndex - 1, SkAdvancedTypefaceMetrics::WidthRange::kRange); } return result.detach(); }