blob: 9129245b11e79420b5ddca303db3b2115fc9839a [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBitmapRegionCanvas.h"
#include "SkCanvas.h"
#include "SkCodecPriv.h"
#include "SkCodecTools.h"
SkBitmapRegionCanvas::SkBitmapRegionCanvas(SkCodec* decoder)
: INHERITED(decoder->getInfo().width(), decoder->getInfo().height())
, fDecoder(decoder)
{}
bool SkBitmapRegionCanvas::decodeRegion(SkBitmap* bitmap, SkBitmap::Allocator* allocator,
const SkIRect& desiredSubset, int sampleSize, SkColorType dstColorType,
bool requireUnpremul) {
// Reject color types not supported by this method
if (kIndex_8_SkColorType == dstColorType || kGray_8_SkColorType == dstColorType) {
SkCodecPrintf("Error: Color type not supported.\n");
return false;
}
// Reject requests for unpremultiplied alpha
if (requireUnpremul) {
SkCodecPrintf("Error: Alpha type not supported.\n");
return false;
}
SkAlphaType dstAlphaType = fDecoder->getInfo().alphaType();
if (kUnpremul_SkAlphaType == dstAlphaType) {
dstAlphaType = kPremul_SkAlphaType;
}
// FIXME: Can we add checks and support kIndex8 or unpremultiplied alpha in special cases?
// Fix the input sampleSize if necessary.
if (sampleSize < 1) {
sampleSize = 1;
}
// The size of the output bitmap is determined by the size of the
// requested subset, not by the size of the intersection of the subset
// and the image dimensions.
// If inputX is negative, we will need to place decoded pixels into the
// output bitmap starting at a left offset. Call this outX.
// If outX is non-zero, subsetX must be zero.
// If inputY is negative, we will need to place decoded pixels into the
// output bitmap starting at a top offset. Call this outY.
// If outY is non-zero, subsetY must be zero.
int outX;
int outY;
SkIRect subset = desiredSubset;
SubsetType type = adjust_subset_rect(fDecoder->getInfo().dimensions(), &subset, &outX, &outY);
if (SubsetType::kOutside_SubsetType == type) {
return false;
}
// Create the image info for the decode
SkImageInfo decodeInfo = SkImageInfo::Make(this->width(), this->height(),
dstColorType, dstAlphaType);
// Start the scanline decoder
SkCodec::Result r = fDecoder->startScanlineDecode(decodeInfo);
if (SkCodec::kSuccess != r) {
SkCodecPrintf("Error: Could not start scanline decoder.\n");
return false;
}
// Allocate a bitmap for the unscaled decode
SkBitmap tmp;
SkImageInfo tmpInfo = decodeInfo.makeWH(this->width(), subset.height());
if (!tmp.tryAllocPixels(tmpInfo)) {
SkCodecPrintf("Error: Could not allocate pixels.\n");
return false;
}
// Skip the unneeded rows
if (!fDecoder->skipScanlines(subset.y())) {
SkCodecPrintf("Error: Failed to skip scanlines.\n");
return false;
}
// Decode the necessary rows
fDecoder->getScanlines(tmp.getAddr(0, 0), subset.height(), tmp.rowBytes());
// Calculate the size of the output
const int outWidth = get_scaled_dimension(desiredSubset.width(), sampleSize);
const int outHeight = get_scaled_dimension(desiredSubset.height(), sampleSize);
// Initialize the destination bitmap
SkImageInfo dstInfo = decodeInfo.makeWH(outWidth, outHeight);
bitmap->setInfo(dstInfo, dstInfo.minRowBytes());
if (!bitmap->tryAllocPixels(allocator, nullptr)) {
SkCodecPrintf("Error: Could not allocate pixels.\n");
return false;
}
// Zero the bitmap if the region is not completely within the image.
// TODO (msarett): Can we make this faster by implementing it to only
// zero parts of the image that we won't overwrite with
// pixels?
// TODO (msarett): This could be skipped if memory is zero initialized.
// This would matter if this code is moved to Android and
// uses Android bitmaps.
if (SubsetType::kPartiallyInside_SubsetType == type) {
bitmap->eraseColor(0);
}
// Use a canvas to crop and scale to the destination bitmap
SkCanvas canvas(*bitmap);
// TODO (msarett): Maybe we can take advantage of the fact that SkRect uses floats?
SkRect src = SkRect::MakeXYWH((SkScalar) subset.x(), (SkScalar) 0,
(SkScalar) subset.width(), (SkScalar) subset.height());
SkRect dst = SkRect::MakeXYWH((SkScalar) (outX / sampleSize), (SkScalar) (outY / sampleSize),
(SkScalar) get_scaled_dimension(subset.width(), sampleSize),
(SkScalar) get_scaled_dimension(subset.height(), sampleSize));
SkPaint paint;
// Overwrite the dst with the src pixels
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
// TODO (msarett): Test multiple filter qualities. kNone is the default.
canvas.drawBitmapRect(tmp, src, dst, &paint);
return true;
}
bool SkBitmapRegionCanvas::conversionSupported(SkColorType colorType) {
// SkCanvas does not draw to these color types.
if (kIndex_8_SkColorType == colorType || kGray_8_SkColorType == colorType) {
return false;
}
// FIXME: Call virtual function when it lands.
SkImageInfo info = SkImageInfo::Make(0, 0, colorType, fDecoder->getInfo().alphaType(),
fDecoder->getInfo().profileType());
return conversion_possible(info, fDecoder->getInfo());
}