1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/utils/SkCanvasStateUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,343 @@ 1.4 +/* 1.5 + * Copyright 2013 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "SkCanvasStateUtils.h" 1.12 + 1.13 +#include "SkBitmapDevice.h" 1.14 +#include "SkCanvas.h" 1.15 +#include "SkCanvasStack.h" 1.16 +#include "SkErrorInternals.h" 1.17 +#include "SkWriter32.h" 1.18 + 1.19 +#define CANVAS_STATE_VERSION 1 1.20 +/* 1.21 + * WARNING: The structs below are part of a stable ABI and as such we explicitly 1.22 + * use unambigious primitives (e.g. int32_t instead of an enum). 1.23 + * 1.24 + * ANY CHANGES TO THE STRUCTS BELOW THAT IMPACT THE ABI SHOULD RESULT IN AN 1.25 + * UPDATE OF THE CANVAS_STATE_VERSION. SUCH CHANGES SHOULD ONLY BE MADE IF 1.26 + * ABSOLUTELY NECESSARY! 1.27 + */ 1.28 +enum RasterConfigs { 1.29 + kUnknown_RasterConfig = 0, 1.30 + kRGB_565_RasterConfig = 1, 1.31 + kARGB_8888_RasterConfig = 2 1.32 +}; 1.33 +typedef int32_t RasterConfig; 1.34 + 1.35 +enum CanvasBackends { 1.36 + kUnknown_CanvasBackend = 0, 1.37 + kRaster_CanvasBackend = 1, 1.38 + kGPU_CanvasBackend = 2, 1.39 + kPDF_CanvasBackend = 3 1.40 +}; 1.41 +typedef int32_t CanvasBackend; 1.42 + 1.43 +struct ClipRect { 1.44 + int32_t left, top, right, bottom; 1.45 +}; 1.46 + 1.47 +struct SkMCState { 1.48 + float matrix[9]; 1.49 + // NOTE: this only works for non-antialiased clips 1.50 + int32_t clipRectCount; 1.51 + ClipRect* clipRects; 1.52 +}; 1.53 + 1.54 +// NOTE: If you add more members, bump CanvasState::version. 1.55 +struct SkCanvasLayerState { 1.56 + CanvasBackend type; 1.57 + int32_t x, y; 1.58 + int32_t width; 1.59 + int32_t height; 1.60 + 1.61 + SkMCState mcState; 1.62 + 1.63 + union { 1.64 + struct { 1.65 + RasterConfig config; // pixel format: a value from RasterConfigs. 1.66 + size_t rowBytes; // Number of bytes from start of one line to next. 1.67 + void* pixels; // The pixels, all (height * rowBytes) of them. 1.68 + } raster; 1.69 + struct { 1.70 + int32_t textureID; 1.71 + } gpu; 1.72 + }; 1.73 +}; 1.74 + 1.75 +class SkCanvasState { 1.76 +public: 1.77 + SkCanvasState(SkCanvas* canvas) { 1.78 + SkASSERT(canvas); 1.79 + version = CANVAS_STATE_VERSION; 1.80 + width = canvas->getDeviceSize().width(); 1.81 + height = canvas->getDeviceSize().height(); 1.82 + layerCount = 0; 1.83 + layers = NULL; 1.84 + originalCanvas = SkRef(canvas); 1.85 + 1.86 + mcState.clipRectCount = 0; 1.87 + mcState.clipRects = NULL; 1.88 + } 1.89 + 1.90 + ~SkCanvasState() { 1.91 + // loop through the layers and free the data allocated to the clipRects 1.92 + for (int i = 0; i < layerCount; ++i) { 1.93 + sk_free(layers[i].mcState.clipRects); 1.94 + } 1.95 + 1.96 + sk_free(mcState.clipRects); 1.97 + sk_free(layers); 1.98 + 1.99 + // it is now safe to free the canvas since there should be no remaining 1.100 + // references to the content that is referenced by this canvas (e.g. pixels) 1.101 + originalCanvas->unref(); 1.102 + } 1.103 + 1.104 + /** 1.105 + * The version this struct was built with. This field must always appear 1.106 + * first in the struct so that when the versions don't match (and the 1.107 + * remaining contents and size are potentially different) we can still 1.108 + * compare the version numbers. 1.109 + */ 1.110 + int32_t version; 1.111 + 1.112 + int32_t width; 1.113 + int32_t height; 1.114 + 1.115 + SkMCState mcState; 1.116 + 1.117 + int32_t layerCount; 1.118 + SkCanvasLayerState* layers; 1.119 + 1.120 +private: 1.121 + SkCanvas* originalCanvas; 1.122 +}; 1.123 + 1.124 +//////////////////////////////////////////////////////////////////////////////// 1.125 + 1.126 +class ClipValidator : public SkCanvas::ClipVisitor { 1.127 +public: 1.128 + ClipValidator() : fFailed(false) {} 1.129 + bool failed() { return fFailed; } 1.130 + 1.131 + // ClipVisitor 1.132 + virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) SK_OVERRIDE { 1.133 + fFailed |= antialias; 1.134 + } 1.135 + 1.136 + virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) SK_OVERRIDE { 1.137 + fFailed |= antialias; 1.138 + } 1.139 + 1.140 + virtual void clipPath(const SkPath&, SkRegion::Op, bool antialias) SK_OVERRIDE { 1.141 + fFailed |= antialias; 1.142 + } 1.143 + 1.144 +private: 1.145 + bool fFailed; 1.146 +}; 1.147 + 1.148 +static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkRegion& clip) { 1.149 + // initialize the struct 1.150 + state->clipRectCount = 0; 1.151 + 1.152 + // capture the matrix 1.153 + for (int i = 0; i < 9; i++) { 1.154 + state->matrix[i] = matrix.get(i); 1.155 + } 1.156 + 1.157 + /* 1.158 + * capture the clip 1.159 + * 1.160 + * storage is allocated on the stack for the first 4 rects. This value was 1.161 + * chosen somewhat arbitrarily, but does allow us to represent simple clips 1.162 + * and some more common complex clips (e.g. a clipRect with a sub-rect 1.163 + * clipped out of its interior) without needing to malloc any additional memory. 1.164 + */ 1.165 + SkSWriter32<4*sizeof(ClipRect)> clipWriter; 1.166 + 1.167 + if (!clip.isEmpty()) { 1.168 + // only returns the b/w clip so aa clips fail 1.169 + SkRegion::Iterator clip_iterator(clip); 1.170 + for (; !clip_iterator.done(); clip_iterator.next()) { 1.171 + // this assumes the SkIRect is stored in l,t,r,b ordering which 1.172 + // matches the ordering of our ClipRect struct 1.173 + clipWriter.writeIRect(clip_iterator.rect()); 1.174 + state->clipRectCount++; 1.175 + } 1.176 + } 1.177 + 1.178 + // allocate memory for the clip then and copy them to the struct 1.179 + state->clipRects = (ClipRect*) sk_malloc_throw(clipWriter.bytesWritten()); 1.180 + clipWriter.flatten(state->clipRects); 1.181 +} 1.182 + 1.183 + 1.184 + 1.185 +SkCanvasState* SkCanvasStateUtils::CaptureCanvasState(SkCanvas* canvas) { 1.186 + SkASSERT(canvas); 1.187 + 1.188 + // Check the clip can be decomposed into rectangles (i.e. no soft clips). 1.189 + ClipValidator validator; 1.190 + canvas->replayClips(&validator); 1.191 + if (validator.failed()) { 1.192 + SkErrorInternals::SetError(kInvalidOperation_SkError, 1.193 + "CaptureCanvasState does not support canvases with antialiased clips.\n"); 1.194 + return NULL; 1.195 + } 1.196 + 1.197 + SkAutoTDelete<SkCanvasState> canvasState(SkNEW_ARGS(SkCanvasState, (canvas))); 1.198 + 1.199 + // decompose the total matrix and clip 1.200 + setup_MC_state(&canvasState->mcState, canvas->getTotalMatrix(), 1.201 + canvas->internal_private_getTotalClip()); 1.202 + 1.203 + /* 1.204 + * decompose the layers 1.205 + * 1.206 + * storage is allocated on the stack for the first 3 layers. It is common in 1.207 + * some view systems (e.g. Android) that a few non-clipped layers are present 1.208 + * and we will not need to malloc any additional memory in those cases. 1.209 + */ 1.210 + SkSWriter32<3*sizeof(SkCanvasLayerState)> layerWriter; 1.211 + int layerCount = 0; 1.212 + for (SkCanvas::LayerIter layer(canvas, true/*skipEmptyClips*/); !layer.done(); layer.next()) { 1.213 + 1.214 + // we currently only work for bitmap backed devices 1.215 + const SkBitmap& bitmap = layer.device()->accessBitmap(true/*changePixels*/); 1.216 + if (bitmap.empty() || bitmap.isNull() || !bitmap.lockPixelsAreWritable()) { 1.217 + return NULL; 1.218 + } 1.219 + 1.220 + SkCanvasLayerState* layerState = 1.221 + (SkCanvasLayerState*) layerWriter.reserve(sizeof(SkCanvasLayerState)); 1.222 + layerState->type = kRaster_CanvasBackend; 1.223 + layerState->x = layer.x(); 1.224 + layerState->y = layer.y(); 1.225 + layerState->width = bitmap.width(); 1.226 + layerState->height = bitmap.height(); 1.227 + 1.228 + switch (bitmap.colorType()) { 1.229 + case kPMColor_SkColorType: 1.230 + layerState->raster.config = kARGB_8888_RasterConfig; 1.231 + break; 1.232 + case kRGB_565_SkColorType: 1.233 + layerState->raster.config = kRGB_565_RasterConfig; 1.234 + break; 1.235 + default: 1.236 + return NULL; 1.237 + } 1.238 + layerState->raster.rowBytes = bitmap.rowBytes(); 1.239 + layerState->raster.pixels = bitmap.getPixels(); 1.240 + 1.241 + setup_MC_state(&layerState->mcState, layer.matrix(), layer.clip()); 1.242 + layerCount++; 1.243 + } 1.244 + 1.245 + // allocate memory for the layers and then and copy them to the struct 1.246 + SkASSERT(layerWriter.bytesWritten() == layerCount * sizeof(SkCanvasLayerState)); 1.247 + canvasState->layerCount = layerCount; 1.248 + canvasState->layers = (SkCanvasLayerState*) sk_malloc_throw(layerWriter.bytesWritten()); 1.249 + layerWriter.flatten(canvasState->layers); 1.250 + 1.251 + // for now, just ignore any client supplied DrawFilter. 1.252 + if (canvas->getDrawFilter()) { 1.253 +// SkDEBUGF(("CaptureCanvasState will ignore the canvases draw filter.\n")); 1.254 + } 1.255 + 1.256 + return canvasState.detach(); 1.257 +} 1.258 + 1.259 +//////////////////////////////////////////////////////////////////////////////// 1.260 + 1.261 +static void setup_canvas_from_MC_state(const SkMCState& state, SkCanvas* canvas) { 1.262 + // reconstruct the matrix 1.263 + SkMatrix matrix; 1.264 + for (int i = 0; i < 9; i++) { 1.265 + matrix.set(i, state.matrix[i]); 1.266 + } 1.267 + 1.268 + // reconstruct the clip 1.269 + SkRegion clip; 1.270 + for (int i = 0; i < state.clipRectCount; ++i) { 1.271 + clip.op(SkIRect::MakeLTRB(state.clipRects[i].left, 1.272 + state.clipRects[i].top, 1.273 + state.clipRects[i].right, 1.274 + state.clipRects[i].bottom), 1.275 + SkRegion::kUnion_Op); 1.276 + } 1.277 + 1.278 + canvas->setMatrix(matrix); 1.279 + canvas->setClipRegion(clip); 1.280 +} 1.281 + 1.282 +static SkCanvas* create_canvas_from_canvas_layer(const SkCanvasLayerState& layerState) { 1.283 + SkASSERT(kRaster_CanvasBackend == layerState.type); 1.284 + 1.285 + SkBitmap bitmap; 1.286 + SkColorType colorType = 1.287 + layerState.raster.config == kARGB_8888_RasterConfig ? kPMColor_SkColorType : 1.288 + layerState.raster.config == kRGB_565_RasterConfig ? kRGB_565_SkColorType : 1.289 + kUnknown_SkColorType; 1.290 + 1.291 + if (colorType == kUnknown_SkColorType) { 1.292 + return NULL; 1.293 + } 1.294 + 1.295 + bitmap.installPixels(SkImageInfo::Make(layerState.width, layerState.height, 1.296 + colorType, kPremul_SkAlphaType), 1.297 + layerState.raster.pixels, layerState.raster.rowBytes, 1.298 + NULL, NULL); 1.299 + 1.300 + SkASSERT(!bitmap.empty()); 1.301 + SkASSERT(!bitmap.isNull()); 1.302 + 1.303 + SkAutoTUnref<SkCanvas> canvas(SkNEW_ARGS(SkCanvas, (bitmap))); 1.304 + 1.305 + // setup the matrix and clip 1.306 + setup_canvas_from_MC_state(layerState.mcState, canvas.get()); 1.307 + 1.308 + return canvas.detach(); 1.309 +} 1.310 + 1.311 +SkCanvas* SkCanvasStateUtils::CreateFromCanvasState(const SkCanvasState* state) { 1.312 + SkASSERT(state); 1.313 + 1.314 + // check that the versions match 1.315 + if (CANVAS_STATE_VERSION != state->version) { 1.316 + SkDebugf("CreateFromCanvasState version does not match the one use to create the input"); 1.317 + return NULL; 1.318 + } 1.319 + 1.320 + if (state->layerCount < 1) { 1.321 + return NULL; 1.322 + } 1.323 + 1.324 + SkAutoTUnref<SkCanvasStack> canvas(SkNEW_ARGS(SkCanvasStack, (state->width, state->height))); 1.325 + 1.326 + // setup the matrix and clip on the n-way canvas 1.327 + setup_canvas_from_MC_state(state->mcState, canvas); 1.328 + 1.329 + // Iterate over the layers and add them to the n-way canvas 1.330 + for (int i = state->layerCount - 1; i >= 0; --i) { 1.331 + SkAutoTUnref<SkCanvas> canvasLayer(create_canvas_from_canvas_layer(state->layers[i])); 1.332 + if (!canvasLayer.get()) { 1.333 + return NULL; 1.334 + } 1.335 + canvas->pushCanvas(canvasLayer.get(), SkIPoint::Make(state->layers[i].x, 1.336 + state->layers[i].y)); 1.337 + } 1.338 + 1.339 + return canvas.detach(); 1.340 +} 1.341 + 1.342 +//////////////////////////////////////////////////////////////////////////////// 1.343 + 1.344 +void SkCanvasStateUtils::ReleaseCanvasState(SkCanvasState* state) { 1.345 + SkDELETE(state); 1.346 +}