1/* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkCanvasStateUtils.h" 9 10#include "SkBitmapDevice.h" 11#include "SkCanvas.h" 12#include "SkCanvasStack.h" 13#include "SkErrorInternals.h" 14#include "SkWriter32.h" 15 16#define CANVAS_STATE_VERSION 1 17/* 18 * WARNING: The structs below are part of a stable ABI and as such we explicitly 19 * use unambigious primitives (e.g. int32_t instead of an enum). 20 * 21 * ANY CHANGES TO THE STRUCTS BELOW THAT IMPACT THE ABI SHOULD RESULT IN AN 22 * UPDATE OF THE CANVAS_STATE_VERSION. SUCH CHANGES SHOULD ONLY BE MADE IF 23 * ABSOLUTELY NECESSARY! 24 */ 25enum RasterConfigs { 26 kUnknown_RasterConfig = 0, 27 kRGB_565_RasterConfig = 1, 28 kARGB_8888_RasterConfig = 2 29}; 30typedef int32_t RasterConfig; 31 32enum CanvasBackends { 33 kUnknown_CanvasBackend = 0, 34 kRaster_CanvasBackend = 1, 35 kGPU_CanvasBackend = 2, 36 kPDF_CanvasBackend = 3 37}; 38typedef int32_t CanvasBackend; 39 40struct ClipRect { 41 int32_t left, top, right, bottom; 42}; 43 44struct SkMCState { 45 float matrix[9]; 46 // NOTE: this only works for non-antialiased clips 47 int32_t clipRectCount; 48 ClipRect* clipRects; 49}; 50 51// NOTE: If you add more members, bump CanvasState::version. 52struct SkCanvasLayerState { 53 CanvasBackend type; 54 int32_t x, y; 55 int32_t width; 56 int32_t height; 57 58 SkMCState mcState; 59 60 union { 61 struct { 62 RasterConfig config; // pixel format: a value from RasterConfigs. 63 size_t rowBytes; // Number of bytes from start of one line to next. 64 void* pixels; // The pixels, all (height * rowBytes) of them. 65 } raster; 66 struct { 67 int32_t textureID; 68 } gpu; 69 }; 70}; 71 72class SkCanvasState { 73public: 74 SkCanvasState(SkCanvas* canvas) { 75 SkASSERT(canvas); 76 version = CANVAS_STATE_VERSION; 77 width = canvas->getDeviceSize().width(); 78 height = canvas->getDeviceSize().height(); 79 layerCount = 0; 80 layers = NULL; 81 originalCanvas = SkRef(canvas); 82 83 mcState.clipRectCount = 0; 84 mcState.clipRects = NULL; 85 } 86 87 ~SkCanvasState() { 88 // loop through the layers and free the data allocated to the clipRects 89 for (int i = 0; i < layerCount; ++i) { 90 sk_free(layers[i].mcState.clipRects); 91 } 92 93 sk_free(mcState.clipRects); 94 sk_free(layers); 95 96 // it is now safe to free the canvas since there should be no remaining 97 // references to the content that is referenced by this canvas (e.g. pixels) 98 originalCanvas->unref(); 99 } 100 101 /** 102 * The version this struct was built with. This field must always appear 103 * first in the struct so that when the versions don't match (and the 104 * remaining contents and size are potentially different) we can still 105 * compare the version numbers. 106 */ 107 int32_t version; 108 109 int32_t width; 110 int32_t height; 111 112 SkMCState mcState; 113 114 int32_t layerCount; 115 SkCanvasLayerState* layers; 116 117private: 118 SkCanvas* originalCanvas; 119}; 120 121//////////////////////////////////////////////////////////////////////////////// 122 123class ClipValidator : public SkCanvas::ClipVisitor { 124public: 125 ClipValidator() : fFailed(false) {} 126 bool failed() { return fFailed; } 127 128 // ClipVisitor 129 virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) SK_OVERRIDE { 130 fFailed |= antialias; 131 } 132 133 virtual void clipPath(const SkPath&, SkRegion::Op, bool antialias) SK_OVERRIDE { 134 fFailed |= antialias; 135 } 136 137private: 138 bool fFailed; 139}; 140 141static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkRegion& clip) { 142 // initialize the struct 143 state->clipRectCount = 0; 144 145 // capture the matrix 146 for (int i = 0; i < 9; i++) { 147 state->matrix[i] = matrix.get(i); 148 } 149 150 /* 151 * capture the clip 152 * 153 * storage is allocated on the stack for the first 4 rects. This value was 154 * chosen somewhat arbitrarily, but does allow us to represent simple clips 155 * and some more common complex clips (e.g. a clipRect with a sub-rect 156 * clipped out of its interior) without needing to malloc any additional memory. 157 */ 158 const int clipBufferSize = 4 * sizeof(ClipRect); 159 char clipBuffer[clipBufferSize]; 160 SkWriter32 clipWriter(sizeof(ClipRect), clipBuffer, clipBufferSize); 161 162 if (!clip.isEmpty()) { 163 // only returns the b/w clip so aa clips fail 164 SkRegion::Iterator clip_iterator(clip); 165 for (; !clip_iterator.done(); clip_iterator.next()) { 166 // this assumes the SkIRect is stored in l,t,r,b ordering which 167 // matches the ordering of our ClipRect struct 168 clipWriter.writeIRect(clip_iterator.rect()); 169 state->clipRectCount++; 170 } 171 } 172 173 // allocate memory for the clip then and copy them to the struct 174 state->clipRects = (ClipRect*) sk_malloc_throw(clipWriter.bytesWritten()); 175 clipWriter.flatten(state->clipRects); 176} 177 178 179 180SkCanvasState* SkCanvasStateUtils::CaptureCanvasState(SkCanvas* canvas) { 181 SkASSERT(canvas); 182 183 // Check the clip can be decomposed into rectangles (i.e. no soft clips). 184 ClipValidator validator; 185 canvas->replayClips(&validator); 186 if (validator.failed()) { 187 SkErrorInternals::SetError(kInvalidOperation_SkError, 188 "CaptureCanvasState does not support canvases with antialiased clips.\n"); 189 return NULL; 190 } 191 192 SkAutoTDelete<SkCanvasState> canvasState(SkNEW_ARGS(SkCanvasState, (canvas))); 193 194 // decompose the total matrix and clip 195 setup_MC_state(&canvasState->mcState, canvas->getTotalMatrix(), canvas->getTotalClip()); 196 197 /* 198 * decompose the layers 199 * 200 * storage is allocated on the stack for the first 3 layers. It is common in 201 * some view systems (e.g. Android) that a few non-clipped layers are present 202 * and we will not need to malloc any additional memory in those cases. 203 */ 204 const int layerBufferSize = 3 * sizeof(SkCanvasLayerState); 205 char layerBuffer[layerBufferSize]; 206 SkWriter32 layerWriter(sizeof(SkCanvasLayerState), layerBuffer, layerBufferSize); 207 int layerCount = 0; 208 for (SkCanvas::LayerIter layer(canvas, true/*skipEmptyClips*/); !layer.done(); layer.next()) { 209 210 // we currently only work for bitmap backed devices 211 const SkBitmap& bitmap = layer.device()->accessBitmap(true/*changePixels*/); 212 if (bitmap.empty() || bitmap.isNull() || !bitmap.lockPixelsAreWritable()) { 213 return NULL; 214 } 215 216 SkCanvasLayerState* layerState = 217 (SkCanvasLayerState*) layerWriter.reserve(sizeof(SkCanvasLayerState)); 218 layerState->type = kRaster_CanvasBackend; 219 layerState->x = layer.x(); 220 layerState->y = layer.y(); 221 layerState->width = bitmap.width(); 222 layerState->height = bitmap.height(); 223 224 switch (bitmap.config()) { 225 case SkBitmap::kARGB_8888_Config: 226 layerState->raster.config = kARGB_8888_RasterConfig; 227 break; 228 case SkBitmap::kRGB_565_Config: 229 layerState->raster.config = kRGB_565_RasterConfig; 230 break; 231 default: 232 return NULL; 233 } 234 layerState->raster.rowBytes = bitmap.rowBytes(); 235 layerState->raster.pixels = bitmap.getPixels(); 236 237 setup_MC_state(&layerState->mcState, layer.matrix(), layer.clip()); 238 layerCount++; 239 } 240 241 // allocate memory for the layers and then and copy them to the struct 242 SkASSERT(layerWriter.bytesWritten() == layerCount * sizeof(SkCanvasLayerState)); 243 canvasState->layerCount = layerCount; 244 canvasState->layers = (SkCanvasLayerState*) sk_malloc_throw(layerWriter.bytesWritten()); 245 layerWriter.flatten(canvasState->layers); 246 247 // for now, just ignore any client supplied DrawFilter. 248 if (canvas->getDrawFilter()) { 249// SkDEBUGF(("CaptureCanvasState will ignore the canvases draw filter.\n")); 250 } 251 252 return canvasState.detach(); 253} 254 255//////////////////////////////////////////////////////////////////////////////// 256 257static void setup_canvas_from_MC_state(const SkMCState& state, SkCanvas* canvas) { 258 // reconstruct the matrix 259 SkMatrix matrix; 260 for (int i = 0; i < 9; i++) { 261 matrix.set(i, state.matrix[i]); 262 } 263 264 // reconstruct the clip 265 SkRegion clip; 266 for (int i = 0; i < state.clipRectCount; ++i) { 267 clip.op(SkIRect::MakeLTRB(state.clipRects[i].left, 268 state.clipRects[i].top, 269 state.clipRects[i].right, 270 state.clipRects[i].bottom), 271 SkRegion::kUnion_Op); 272 } 273 274 canvas->setMatrix(matrix); 275 canvas->setClipRegion(clip); 276} 277 278static SkCanvas* create_canvas_from_canvas_layer(const SkCanvasLayerState& layerState) { 279 SkASSERT(kRaster_CanvasBackend == layerState.type); 280 281 SkBitmap bitmap; 282 SkBitmap::Config config = 283 layerState.raster.config == kARGB_8888_RasterConfig ? SkBitmap::kARGB_8888_Config : 284 layerState.raster.config == kRGB_565_RasterConfig ? SkBitmap::kRGB_565_Config : 285 SkBitmap::kNo_Config; 286 287 if (config == SkBitmap::kNo_Config) { 288 return NULL; 289 } 290 291 bitmap.setConfig(config, layerState.width, layerState.height, 292 layerState.raster.rowBytes); 293 bitmap.setPixels(layerState.raster.pixels); 294 295 SkASSERT(!bitmap.empty()); 296 SkASSERT(!bitmap.isNull()); 297 298 // create a device & canvas 299 SkAutoTUnref<SkBitmapDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap))); 300 SkAutoTUnref<SkCanvas> canvas(SkNEW_ARGS(SkCanvas, (device.get()))); 301 302 // setup the matrix and clip 303 setup_canvas_from_MC_state(layerState.mcState, canvas.get()); 304 305 return canvas.detach(); 306} 307 308SkCanvas* SkCanvasStateUtils::CreateFromCanvasState(const SkCanvasState* state) { 309 SkASSERT(state); 310 311 // check that the versions match 312 if (CANVAS_STATE_VERSION != state->version) { 313 SkDebugf("CreateFromCanvasState version does not match the one use to create the input"); 314 return NULL; 315 } 316 317 if (state->layerCount < 1) { 318 return NULL; 319 } 320 321 SkAutoTUnref<SkCanvasStack> canvas(SkNEW_ARGS(SkCanvasStack, (state->width, state->height))); 322 323 // setup the matrix and clip on the n-way canvas 324 setup_canvas_from_MC_state(state->mcState, canvas); 325 326 // Iterate over the layers and add them to the n-way canvas 327 for (int i = state->layerCount - 1; i >= 0; --i) { 328 SkAutoTUnref<SkCanvas> canvasLayer(create_canvas_from_canvas_layer(state->layers[i])); 329 if (!canvasLayer.get()) { 330 return NULL; 331 } 332 canvas->pushCanvas(canvasLayer.get(), SkIPoint::Make(state->layers[i].x, 333 state->layers[i].y)); 334 } 335 336 return canvas.detach(); 337} 338 339//////////////////////////////////////////////////////////////////////////////// 340 341void SkCanvasStateUtils::ReleaseCanvasState(SkCanvasState* state) { 342 SkDELETE(state); 343} 344