michael@0: michael@0: /* michael@0: * Copyright 2007 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "SkPictureFlat.h" michael@0: #include "SkPicturePlayback.h" michael@0: #include "SkPictureRecord.h" michael@0: michael@0: #include "SkBitmapDevice.h" michael@0: #include "SkCanvas.h" michael@0: #include "SkChunkAlloc.h" michael@0: #include "SkPicture.h" michael@0: #include "SkRegion.h" michael@0: #include "SkStream.h" michael@0: #include "SkTDArray.h" michael@0: #include "SkTSearch.h" michael@0: #include "SkTime.h" michael@0: michael@0: #include "SkReader32.h" michael@0: #include "SkWriter32.h" michael@0: #include "SkRTree.h" michael@0: #include "SkBBoxHierarchyRecord.h" michael@0: michael@0: #define DUMP_BUFFER_SIZE 65536 michael@0: michael@0: //#define ENABLE_TIME_DRAW // dumps milliseconds for each draw michael@0: michael@0: michael@0: #ifdef SK_DEBUG michael@0: // enable SK_DEBUG_TRACE to trace DrawType elements when michael@0: // recorded and played back michael@0: // #define SK_DEBUG_TRACE michael@0: // enable SK_DEBUG_SIZE to see the size of picture components michael@0: // #define SK_DEBUG_SIZE michael@0: // enable SK_DEBUG_DUMP to see the contents of recorded elements michael@0: // #define SK_DEBUG_DUMP michael@0: // enable SK_DEBUG_VALIDATE to check internal structures for consistency michael@0: // #define SK_DEBUG_VALIDATE michael@0: #endif michael@0: michael@0: #if defined SK_DEBUG_TRACE || defined SK_DEBUG_DUMP michael@0: const char* DrawTypeToString(DrawType drawType) { michael@0: switch (drawType) { michael@0: case UNUSED: SkDebugf("DrawType UNUSED\n"); SkASSERT(0); break; michael@0: case CLIP_PATH: return "CLIP_PATH"; michael@0: case CLIP_REGION: return "CLIP_REGION"; michael@0: case CLIP_RECT: return "CLIP_RECT"; michael@0: case CLIP_RRECT: return "CLIP_RRECT"; michael@0: case CONCAT: return "CONCAT"; michael@0: case DRAW_BITMAP: return "DRAW_BITMAP"; michael@0: case DRAW_BITMAP_MATRIX: return "DRAW_BITMAP_MATRIX"; michael@0: case DRAW_BITMAP_NINE: return "DRAW_BITMAP_NINE"; michael@0: case DRAW_BITMAP_RECT_TO_RECT: return "DRAW_BITMAP_RECT_TO_RECT"; michael@0: case DRAW_CLEAR: return "DRAW_CLEAR"; michael@0: case DRAW_DATA: return "DRAW_DATA"; michael@0: case DRAW_OVAL: return "DRAW_OVAL"; michael@0: case DRAW_PAINT: return "DRAW_PAINT"; michael@0: case DRAW_PATH: return "DRAW_PATH"; michael@0: case DRAW_PICTURE: return "DRAW_PICTURE"; michael@0: case DRAW_POINTS: return "DRAW_POINTS"; michael@0: case DRAW_POS_TEXT: return "DRAW_POS_TEXT"; michael@0: case DRAW_POS_TEXT_TOP_BOTTOM: return "DRAW_POS_TEXT_TOP_BOTTOM"; michael@0: case DRAW_POS_TEXT_H: return "DRAW_POS_TEXT_H"; michael@0: case DRAW_POS_TEXT_H_TOP_BOTTOM: return "DRAW_POS_TEXT_H_TOP_BOTTOM"; michael@0: case DRAW_RECT: return "DRAW_RECT"; michael@0: case DRAW_RRECT: return "DRAW_RRECT"; michael@0: case DRAW_SPRITE: return "DRAW_SPRITE"; michael@0: case DRAW_TEXT: return "DRAW_TEXT"; michael@0: case DRAW_TEXT_ON_PATH: return "DRAW_TEXT_ON_PATH"; michael@0: case DRAW_TEXT_TOP_BOTTOM: return "DRAW_TEXT_TOP_BOTTOM"; michael@0: case DRAW_VERTICES: return "DRAW_VERTICES"; michael@0: case RESTORE: return "RESTORE"; michael@0: case ROTATE: return "ROTATE"; michael@0: case SAVE: return "SAVE"; michael@0: case SAVE_LAYER: return "SAVE_LAYER"; michael@0: case SCALE: return "SCALE"; michael@0: case SET_MATRIX: return "SET_MATRIX"; michael@0: case SKEW: return "SKEW"; michael@0: case TRANSLATE: return "TRANSLATE"; michael@0: case NOOP: return "NOOP"; michael@0: default: michael@0: SkDebugf("DrawType error 0x%08x\n", drawType); michael@0: SkASSERT(0); michael@0: break; michael@0: } michael@0: SkASSERT(0); michael@0: return NULL; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef SK_DEBUG_VALIDATE michael@0: static void validateMatrix(const SkMatrix* matrix) { michael@0: SkScalar scaleX = matrix->getScaleX(); michael@0: SkScalar scaleY = matrix->getScaleY(); michael@0: SkScalar skewX = matrix->getSkewX(); michael@0: SkScalar skewY = matrix->getSkewY(); michael@0: SkScalar perspX = matrix->getPerspX(); michael@0: SkScalar perspY = matrix->getPerspY(); michael@0: if (scaleX != 0 && skewX != 0) michael@0: SkDebugf("scaleX != 0 && skewX != 0\n"); michael@0: SkASSERT(scaleX == 0 || skewX == 0); michael@0: SkASSERT(scaleY == 0 || skewY == 0); michael@0: SkASSERT(perspX == 0); michael@0: SkASSERT(perspY == 0); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkPicture::SkPicture() { michael@0: fRecord = NULL; michael@0: fPlayback = NULL; michael@0: fWidth = fHeight = 0; michael@0: fAccelData = NULL; michael@0: } michael@0: michael@0: SkPicture::SkPicture(const SkPicture& src) michael@0: : INHERITED() michael@0: , fAccelData(NULL) { michael@0: fWidth = src.fWidth; michael@0: fHeight = src.fHeight; michael@0: fRecord = NULL; michael@0: michael@0: /* We want to copy the src's playback. However, if that hasn't been built michael@0: yet, we need to fake a call to endRecording() without actually calling michael@0: it (since it is destructive, and we don't want to change src). michael@0: */ michael@0: if (src.fPlayback) { michael@0: fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback)); michael@0: } else if (src.fRecord) { michael@0: // here we do a fake src.endRecording() michael@0: fPlayback = SkNEW_ARGS(SkPicturePlayback, (*src.fRecord)); michael@0: } else { michael@0: fPlayback = NULL; michael@0: } michael@0: } michael@0: michael@0: SkPicture::~SkPicture() { michael@0: SkSafeUnref(fRecord); michael@0: SkDELETE(fPlayback); michael@0: SkSafeUnref(fAccelData); michael@0: } michael@0: michael@0: void SkPicture::internalOnly_EnableOpts(bool enableOpts) { michael@0: if (NULL != fRecord) { michael@0: fRecord->internalOnly_EnableOpts(enableOpts); michael@0: } michael@0: } michael@0: michael@0: void SkPicture::swap(SkPicture& other) { michael@0: SkTSwap(fRecord, other.fRecord); michael@0: SkTSwap(fPlayback, other.fPlayback); michael@0: SkTSwap(fAccelData, other.fAccelData); michael@0: SkTSwap(fWidth, other.fWidth); michael@0: SkTSwap(fHeight, other.fHeight); michael@0: } michael@0: michael@0: SkPicture* SkPicture::clone() const { michael@0: SkPicture* clonedPicture = SkNEW(SkPicture); michael@0: clone(clonedPicture, 1); michael@0: return clonedPicture; michael@0: } michael@0: michael@0: void SkPicture::clone(SkPicture* pictures, int count) const { michael@0: SkPictCopyInfo copyInfo; michael@0: michael@0: for (int i = 0; i < count; i++) { michael@0: SkPicture* clone = &pictures[i]; michael@0: michael@0: clone->fWidth = fWidth; michael@0: clone->fHeight = fHeight; michael@0: SkSafeSetNull(clone->fRecord); michael@0: SkDELETE(clone->fPlayback); michael@0: michael@0: /* We want to copy the src's playback. However, if that hasn't been built michael@0: yet, we need to fake a call to endRecording() without actually calling michael@0: it (since it is destructive, and we don't want to change src). michael@0: */ michael@0: if (fPlayback) { michael@0: clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fPlayback, ©Info)); michael@0: } else if (fRecord) { michael@0: // here we do a fake src.endRecording() michael@0: clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord, true)); michael@0: } else { michael@0: clone->fPlayback = NULL; michael@0: } michael@0: } michael@0: } michael@0: michael@0: SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { michael@0: static int32_t gNextID = 0; michael@0: michael@0: int32_t id = sk_atomic_inc(&gNextID); michael@0: if (id >= 1 << (8 * sizeof(Domain))) { michael@0: SK_CRASH(); michael@0: } michael@0: michael@0: return static_cast(id); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkCanvas* SkPicture::beginRecording(int width, int height, michael@0: uint32_t recordingFlags) { michael@0: if (fPlayback) { michael@0: SkDELETE(fPlayback); michael@0: fPlayback = NULL; michael@0: } michael@0: SkSafeUnref(fAccelData); michael@0: SkSafeSetNull(fRecord); michael@0: michael@0: // Must be set before calling createBBoxHierarchy michael@0: fWidth = width; michael@0: fHeight = height; michael@0: michael@0: const SkISize size = SkISize::Make(width, height); michael@0: michael@0: if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) { michael@0: SkBBoxHierarchy* tree = this->createBBoxHierarchy(); michael@0: SkASSERT(NULL != tree); michael@0: fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (size, recordingFlags, tree)); michael@0: tree->unref(); michael@0: } else { michael@0: fRecord = SkNEW_ARGS(SkPictureRecord, (size, recordingFlags)); michael@0: } michael@0: fRecord->beginRecording(); michael@0: michael@0: return fRecord; michael@0: } michael@0: michael@0: SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const { michael@0: // These values were empirically determined to produce reasonable michael@0: // performance in most cases. michael@0: static const int kRTreeMinChildren = 6; michael@0: static const int kRTreeMaxChildren = 11; michael@0: michael@0: SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth), michael@0: SkIntToScalar(fHeight)); michael@0: bool sortDraws = false; // Do not sort draw calls when bulk loading. michael@0: michael@0: return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren, michael@0: aspectRatio, sortDraws); michael@0: } michael@0: michael@0: SkCanvas* SkPicture::getRecordingCanvas() const { michael@0: // will be null if we are not recording michael@0: return fRecord; michael@0: } michael@0: michael@0: void SkPicture::endRecording() { michael@0: if (NULL == fPlayback) { michael@0: if (NULL != fRecord) { michael@0: fRecord->endRecording(); michael@0: fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); michael@0: SkSafeSetNull(fRecord); michael@0: } michael@0: } michael@0: SkASSERT(NULL == fRecord); michael@0: } michael@0: michael@0: void SkPicture::draw(SkCanvas* surface, SkDrawPictureCallback* callback) { michael@0: this->endRecording(); michael@0: if (NULL != fPlayback) { michael@0: fPlayback->draw(*surface, callback); michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include "SkStream.h" michael@0: michael@0: static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; michael@0: michael@0: bool SkPicture::IsValidPictInfo(const SkPictInfo& info) { michael@0: if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) { michael@0: return false; michael@0: } michael@0: michael@0: if (info.fVersion < MIN_PICTURE_VERSION || michael@0: info.fVersion > CURRENT_PICTURE_VERSION) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { michael@0: if (NULL == stream) { michael@0: return false; michael@0: } michael@0: michael@0: // Check magic bytes. michael@0: SkPictInfo info; michael@0: SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); michael@0: if (!stream->read(&info, sizeof(info)) || !IsValidPictInfo(info)) { michael@0: return false; michael@0: } michael@0: michael@0: if (pInfo != NULL) { michael@0: *pInfo = info; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer& buffer, SkPictInfo* pInfo) { michael@0: // Check magic bytes. michael@0: SkPictInfo info; michael@0: SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); michael@0: if (!buffer.readByteArray(&info, sizeof(info)) || !IsValidPictInfo(info)) { michael@0: return false; michael@0: } michael@0: michael@0: if (pInfo != NULL) { michael@0: *pInfo = info; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: SkPicture::SkPicture(SkPicturePlayback* playback, int width, int height) michael@0: : fPlayback(playback) michael@0: , fRecord(NULL) michael@0: , fWidth(width) michael@0: , fHeight(height) michael@0: , fAccelData(NULL) {} michael@0: michael@0: SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { michael@0: SkPictInfo info; michael@0: michael@0: if (!InternalOnly_StreamIsSKP(stream, &info)) { michael@0: return NULL; michael@0: } michael@0: michael@0: SkPicturePlayback* playback; michael@0: // Check to see if there is a playback to recreate. michael@0: if (stream->readBool()) { michael@0: playback = SkPicturePlayback::CreateFromStream(stream, info, proc); michael@0: if (NULL == playback) { michael@0: return NULL; michael@0: } michael@0: } else { michael@0: playback = NULL; michael@0: } michael@0: michael@0: return SkNEW_ARGS(SkPicture, (playback, info.fWidth, info.fHeight)); michael@0: } michael@0: michael@0: SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { michael@0: SkPictInfo info; michael@0: michael@0: if (!InternalOnly_BufferIsSKP(buffer, &info)) { michael@0: return NULL; michael@0: } michael@0: michael@0: SkPicturePlayback* playback; michael@0: // Check to see if there is a playback to recreate. michael@0: if (buffer.readBool()) { michael@0: playback = SkPicturePlayback::CreateFromBuffer(buffer); michael@0: if (NULL == playback) { michael@0: return NULL; michael@0: } michael@0: } else { michael@0: playback = NULL; michael@0: } michael@0: michael@0: return SkNEW_ARGS(SkPicture, (playback, info.fWidth, info.fHeight)); michael@0: } michael@0: michael@0: void SkPicture::createHeader(SkPictInfo* info) const { michael@0: // Copy magic bytes at the beginning of the header michael@0: SkASSERT(sizeof(kMagic) == 8); michael@0: SkASSERT(sizeof(kMagic) == sizeof(info->fMagic)); michael@0: memcpy(info->fMagic, kMagic, sizeof(kMagic)); michael@0: michael@0: // Set picture info after magic bytes in the header michael@0: info->fVersion = CURRENT_PICTURE_VERSION; michael@0: info->fWidth = fWidth; michael@0: info->fHeight = fHeight; michael@0: info->fFlags = SkPictInfo::kCrossProcess_Flag; michael@0: // TODO: remove this flag, since we're always float (now) michael@0: info->fFlags |= SkPictInfo::kScalarIsFloat_Flag; michael@0: michael@0: if (8 == sizeof(void*)) { michael@0: info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag; michael@0: } michael@0: } michael@0: michael@0: void SkPicture::serialize(SkWStream* stream, EncodeBitmap encoder) const { michael@0: SkPicturePlayback* playback = fPlayback; michael@0: michael@0: if (NULL == playback && fRecord) { michael@0: playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); michael@0: } michael@0: michael@0: SkPictInfo header; michael@0: this->createHeader(&header); michael@0: stream->write(&header, sizeof(header)); michael@0: if (playback) { michael@0: stream->writeBool(true); michael@0: playback->serialize(stream, encoder); michael@0: // delete playback if it is a local version (i.e. cons'd up just now) michael@0: if (playback != fPlayback) { michael@0: SkDELETE(playback); michael@0: } michael@0: } else { michael@0: stream->writeBool(false); michael@0: } michael@0: } michael@0: michael@0: void SkPicture::flatten(SkWriteBuffer& buffer) const { michael@0: SkPicturePlayback* playback = fPlayback; michael@0: michael@0: if (NULL == playback && fRecord) { michael@0: playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord)); michael@0: } michael@0: michael@0: SkPictInfo header; michael@0: this->createHeader(&header); michael@0: buffer.writeByteArray(&header, sizeof(header)); michael@0: if (playback) { michael@0: buffer.writeBool(true); michael@0: playback->flatten(buffer); michael@0: // delete playback if it is a local version (i.e. cons'd up just now) michael@0: if (playback != fPlayback) { michael@0: SkDELETE(playback); michael@0: } michael@0: } else { michael@0: buffer.writeBool(false); michael@0: } michael@0: } michael@0: michael@0: bool SkPicture::willPlayBackBitmaps() const { michael@0: if (!fPlayback) { michael@0: return false; michael@0: } michael@0: return fPlayback->containsBitmaps(); michael@0: } michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: void SkPicture::abortPlayback() { michael@0: if (NULL == fPlayback) { michael@0: return; michael@0: } michael@0: fPlayback->abort(); michael@0: } michael@0: #endif