blob: 9ef0b4af30229e570bebe98ab32885effa98f1b1 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config.h"
#include "NinePatchPeeker.h"
#include "RenderSkinNinePatch.h"
#include "SkCanvas.h"
#include "SkImageDecoder.h"
#include "SkNinePatch.h"
#include "SkRect.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include <androidfw/Asset.h>
#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
#include <utils/Log.h>
class SkPaint;
class SkRegion;
using namespace android;
extern void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds,
const SkBitmap& bitmap, const Res_png_9patch& chunk,
const SkPaint* paint, SkRegion** outRegion);
bool RenderSkinNinePatch::decodeAsset(AssetManager* am, const char* filename, NinePatch* ninepatch) {
Asset* asset = am->open(filename, android::Asset::ACCESS_BUFFER);
if (!asset) {
asset = am->openNonAsset(filename, android::Asset::ACCESS_BUFFER);
if (!asset) {
return false;
}
}
SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
SkMemoryStream stream(asset->getBuffer(false), asset->getLength());
SkImageDecoder* decoder = SkImageDecoder::Factory(&stream);
if (!decoder) {
asset->close();
ALOGE("RenderSkinNinePatch::Failed to create an image decoder");
return false;
}
decoder->setSampleSize(1);
decoder->setDitherImage(true);
decoder->setPreferQualityOverSpeed(false);
NinePatchPeeker peeker(decoder);
SkAutoTDelete<SkImageDecoder> add(decoder);
decoder->setPeeker(&peeker);
if (!decoder->decode(&stream, &ninepatch->m_bitmap, prefConfig, mode, true)) {
asset->close();
ALOGE("RenderSkinNinePatch::Failed to decode nine patch asset");
return false;
}
asset->close();
if (!peeker.fPatch) {
ALOGE("RenderSkinNinePatch::Patch data not valid");
return false;
}
void** data = &ninepatch->m_serializedPatchData;
*data = malloc(peeker.fPatch->serializedSize());
peeker.fPatch->serialize(*data);
return true;
}
void RenderSkinNinePatch::DrawNinePatch(SkCanvas* canvas, const SkRect& bounds,
const NinePatch& patch) {
Res_png_9patch* data = Res_png_9patch::deserialize(patch.m_serializedPatchData);
// if the NinePatch is bigger than the destination on a given axis the default
// decoder will not stretch properly, therefore we fall back to skia's decoder
// which if needed will down-sample and draw the bitmap as best as possible.
if (patch.m_bitmap.width() >= bounds.width() || patch.m_bitmap.height() >= bounds.height()) {
SkPaint defaultPaint;
// matches default dither in NinePatchDrawable.java.
defaultPaint.setDither(true);
SkNinePatch::DrawMesh(canvas, bounds, patch.m_bitmap,
data->xDivs, data->numXDivs,
data->yDivs, data->numYDivs,
&defaultPaint);
} else {
NinePatch_Draw(canvas, bounds, patch.m_bitmap, *data, 0, 0);
}
}