/*
**
** Copyright 2007, 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 "SkPictureFlat.h"
#include "SkPicturePlayback.h"
#include "SkPictureRecord.h"
#include "SkCanvas.h"
#include "SkChunkAlloc.h"
#include "SkPicture.h"
#include "SkRegion.h"
#include "SkStream.h"
#include "SkTDArray.h"
#include "SkTSearch.h"
#include "SkTime.h"
#include "SkReader32.h"
#include "SkWriter32.h"
#define DUMP_BUFFER_SIZE 65536
//#define ENABLE_TIME_DRAW // dumps milliseconds for each draw
#ifdef SK_DEBUG
// enable SK_DEBUG_TRACE to trace DrawType elements when
// recorded and played back
// #define SK_DEBUG_TRACE
// enable SK_DEBUG_SIZE to see the size of picture components
// #define SK_DEBUG_SIZE
// enable SK_DEBUG_DUMP to see the contents of recorded elements
// #define SK_DEBUG_DUMP
// enable SK_DEBUG_VALIDATE to check internal structures for consistency
// #define SK_DEBUG_VALIDATE
#endif
#if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP
const char* DrawTypeToString(DrawType drawType) {
switch (drawType) {
case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break;
case CLIP_PATH: return "CLIP_PATH";
case CLIP_REGION: return "CLIP_REGION";
case CLIP_RECT: return "CLIP_RECT";
case CONCAT: return "CONCAT";
case DRAW_BITMAP: return "DRAW_BITMAP";
case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX";
case DRAW_BITMAP_RECT: return "DRAW_BITMAP_RECT";
case DRAW_PAINT: return "DRAW_PAINT";
case DRAW_PATH: return "DRAW_PATH";
case DRAW_PICTURE: return "DRAW_PICTURE";
case DRAW_POINTS: return "DRAW_POINTS";
case DRAW_POS_TEXT: return "DRAW_POS_TEXT";
case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H";
case DRAW_RECT_GENERAL: return "DRAW_RECT_GENERAL";
case DRAW_RECT_SIMPLE: return "DRAW_RECT_SIMPLE";
case DRAW_SPRITE: return "DRAW_SPRITE";
case DRAW_TEXT: return "DRAW_TEXT";
case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH";
case RESTORE: return "RESTORE";
case ROTATE: return "ROTATE";
case SAVE: return "SAVE";
case SAVE_LAYER: return "SAVE_LAYER";
case SCALE: return "SCALE";
case SKEW: return "SKEW";
case TRANSLATE: return "TRANSLATE";
default:
SkDebugf("DrawType error 0x%08x\n", drawType);
SkASSERT(0);
break;
}
SkASSERT(0);
return NULL;
}
#endif
#ifdef SK_DEBUG_VALIDATE
static void validateMatrix(const SkMatrix* matrix) {
SkScalar scaleX = matrix->getScaleX();
SkScalar scaleY = matrix->getScaleY();
SkScalar skewX = matrix->getSkewX();
SkScalar skewY = matrix->getSkewY();
SkScalar perspX = matrix->getPerspX();
SkScalar perspY = matrix->getPerspY();
if (scaleX != 0 && skewX != 0)
SkDebugf("scaleX != 0 && skewX != 0\n");
SkASSERT(scaleX == 0 || skewX == 0);
SkASSERT(scaleY == 0 || skewY == 0);
SkASSERT(perspX == 0);
SkASSERT(perspY == 0);
}
#endif
///////////////////////////////////////////////////////////////////////////////
SkPicture::SkPicture() {
fRecord = NULL;
fPlayback = NULL;
fWidth = fHeight = 0;
}
SkPicture::SkPicture(const SkPicture& src) : SkRefCnt() {
fWidth = src.fWidth;
fHeight = src.fHeight;
fRecord = NULL;
/* We want to copy the src's playback. However, if that hasn't been built
yet, we need to fake a call to endRecording() without actually calling
it (since it is destructive, and we don't want to change src).
*/
if (src.fPlayback) {
fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback));
} else if (src.fRecord) {
// here we do a fake src.endRecording()
fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord));
} else {
fPlayback = NULL;
}
}
SkPicture::~SkPicture() {
fRecord->safeUnref();
SkDELETE(fPlayback);
}
void SkPicture::swap(SkPicture& other) {
SkTSwap(fRecord, other.fRecord);
SkTSwap(fPlayback, other.fPlayback);
SkTSwap(fWidth, other.fWidth);
SkTSwap(fHeight, other.fHeight);
}
///////////////////////////////////////////////////////////////////////////////
SkCanvas* SkPicture::beginRecording(int width, int height,
uint32_t recordingFlags) {
if (fPlayback) {
SkDELETE(fPlayback);
fPlayback = NULL;
}
if (NULL != fRecord) {
fRecord->unref();
fRecord = NULL;
}
fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags));
fWidth = width;
fHeight = height;
SkBitmap bm;
bm.setConfig(SkBitmap::kNo_Config, width, height);
fRecord->setBitmapDevice(bm);
return fRecord;
}
SkCanvas* SkPicture::getRecordingCanvas() const {
// will be null if we are not recording
return fRecord;
}
void SkPicture::endRecording() {
if (NULL == fPlayback) {
if (NULL != fRecord) {
fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
fRecord->unref();
fRecord = NULL;
}
}
SkASSERT(NULL == fRecord);
}
void SkPicture::draw(SkCanvas* surface) {
this->endRecording();
if (fPlayback) {
fPlayback->draw(*surface);
}
}
///////////////////////////////////////////////////////////////////////////////
#include "SkStream.h"
#define PICTURE_VERSION 1
SkPicture::SkPicture(SkStream* stream) : SkRefCnt() {
if (stream->readU32() != PICTURE_VERSION) {
sk_throw();
}
fWidth = stream->readU32();
fHeight = stream->readU32();
fRecord = NULL;
fPlayback = NULL;
if (stream->readBool()) {
fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream));
}
}
void SkPicture::serialize(SkWStream* stream) const {
SkPicturePlayback* playback = fPlayback;
if (NULL == playback && fRecord) {
playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
}
stream->write32(PICTURE_VERSION);
stream->write32(fWidth);
stream->write32(fHeight);
if (playback) {
stream->writeBool(true);
playback->serialize(stream);
// delete playback if it is a local version (i.e. cons'd up just now)
if (playback != fPlayback) {
SkDELETE(playback);
}
} else {
stream->writeBool(false);
}
}
void SkPicture::abortPlayback() {
if (NULL == fPlayback) {
return;
}
fPlayback->abort();
}