Home | History | Annotate | Download | only in utils
      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 "SkCanvas.h"
     11 #include "SkCanvasStack.h"
     12 #include "SkDevice.h"
     13 #include "SkRasterClip.h"
     14 #include "SkWriter32.h"
     15 #include "SkClipOpPriv.h"
     16 
     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 A NEW
     22  * NEW SUBCLASS OF SkCanvasState. SUCH CHANGES SHOULD ONLY BE MADE IF ABSOLUTELY
     23  * NECESSARY!
     24  *
     25  * In order to test changes, run the CanvasState tests. gyp/canvas_state_lib.gyp
     26  * describes how to create a library to pass to the CanvasState tests. The tests
     27  * should succeed when building the library with your changes and passing that to
     28  * the tests running in the unchanged Skia.
     29  */
     30 enum RasterConfigs {
     31   kUnknown_RasterConfig   = 0,
     32   kRGB_565_RasterConfig   = 1,
     33   kARGB_8888_RasterConfig = 2
     34 };
     35 typedef int32_t RasterConfig;
     36 
     37 enum CanvasBackends {
     38     kUnknown_CanvasBackend = 0,
     39     kRaster_CanvasBackend  = 1,
     40     kGPU_CanvasBackend     = 2,
     41     kPDF_CanvasBackend     = 3
     42 };
     43 typedef int32_t CanvasBackend;
     44 
     45 struct ClipRect {
     46     int32_t left, top, right, bottom;
     47 };
     48 
     49 struct SkMCState {
     50     float matrix[9];
     51     // NOTE: this only works for non-antialiased clips
     52     int32_t clipRectCount;
     53     ClipRect* clipRects;
     54 };
     55 
     56 // NOTE: If you add more members, create a new subclass of SkCanvasState with a
     57 // new CanvasState::version.
     58 struct SkCanvasLayerState {
     59     CanvasBackend type;
     60     int32_t x, y;
     61     int32_t width;
     62     int32_t height;
     63 
     64     SkMCState mcState;
     65 
     66     union {
     67         struct {
     68             RasterConfig config; // pixel format: a value from RasterConfigs.
     69             uint64_t rowBytes;   // Number of bytes from start of one line to next.
     70             void* pixels;        // The pixels, all (height * rowBytes) of them.
     71         } raster;
     72         struct {
     73             int32_t textureID;
     74         } gpu;
     75     };
     76 };
     77 
     78 class SkCanvasState {
     79 public:
     80     SkCanvasState(int32_t version, SkCanvas* canvas) {
     81         SkASSERT(canvas);
     82         this->version = version;
     83         width = canvas->getBaseLayerSize().width();
     84         height = canvas->getBaseLayerSize().height();
     85 
     86     }
     87 
     88     /**
     89      * The version this struct was built with.  This field must always appear
     90      * first in the struct so that when the versions don't match (and the
     91      * remaining contents and size are potentially different) we can still
     92      * compare the version numbers.
     93      */
     94     int32_t version;
     95     int32_t width;
     96     int32_t height;
     97     int32_t alignmentPadding;
     98 };
     99 
    100 class SkCanvasState_v1 : public SkCanvasState {
    101 public:
    102     static const int32_t kVersion = 1;
    103 
    104     SkCanvasState_v1(SkCanvas* canvas) : INHERITED(kVersion, canvas) {
    105         layerCount = 0;
    106         layers = nullptr;
    107         mcState.clipRectCount = 0;
    108         mcState.clipRects = nullptr;
    109         originalCanvas = canvas;
    110     }
    111 
    112     ~SkCanvasState_v1() {
    113         // loop through the layers and free the data allocated to the clipRects
    114         for (int i = 0; i < layerCount; ++i) {
    115             sk_free(layers[i].mcState.clipRects);
    116         }
    117 
    118         sk_free(mcState.clipRects);
    119         sk_free(layers);
    120     }
    121 
    122     SkMCState mcState;
    123 
    124     int32_t layerCount;
    125     SkCanvasLayerState* layers;
    126 private:
    127     SkCanvas* originalCanvas;
    128     typedef SkCanvasState INHERITED;
    129 };
    130 
    131 ////////////////////////////////////////////////////////////////////////////////
    132 
    133 static void setup_MC_state(SkMCState* state, const SkMatrix& matrix, const SkRegion& clip) {
    134     // initialize the struct
    135     state->clipRectCount = 0;
    136 
    137     // capture the matrix
    138     for (int i = 0; i < 9; i++) {
    139         state->matrix[i] = matrix.get(i);
    140     }
    141 
    142     /*
    143      * capture the clip
    144      *
    145      * storage is allocated on the stack for the first 4 rects. This value was
    146      * chosen somewhat arbitrarily, but does allow us to represent simple clips
    147      * and some more common complex clips (e.g. a clipRect with a sub-rect
    148      * clipped out of its interior) without needing to malloc any additional memory.
    149      */
    150     SkSWriter32<4*sizeof(ClipRect)> clipWriter;
    151 
    152     if (!clip.isEmpty()) {
    153         // only returns the b/w clip so aa clips fail
    154         SkRegion::Iterator clip_iterator(clip);
    155         for (; !clip_iterator.done(); clip_iterator.next()) {
    156             // this assumes the SkIRect is stored in l,t,r,b ordering which
    157             // matches the ordering of our ClipRect struct
    158             clipWriter.writeIRect(clip_iterator.rect());
    159             state->clipRectCount++;
    160         }
    161     }
    162 
    163     // allocate memory for the clip then and copy them to the struct
    164     state->clipRects = (ClipRect*) sk_malloc_throw(clipWriter.bytesWritten());
    165     clipWriter.flatten(state->clipRects);
    166 }
    167 
    168 
    169 
    170 SkCanvasState* SkCanvasStateUtils::CaptureCanvasState(SkCanvas* canvas) {
    171     SkASSERT(canvas);
    172 
    173     // Check the clip can be decomposed into rectangles (i.e. no soft clips).
    174     if (canvas->androidFramework_isClipAA()) {
    175         return nullptr;
    176     }
    177 
    178     std::unique_ptr<SkCanvasState_v1> canvasState(new SkCanvasState_v1(canvas));
    179 
    180     // decompose the total matrix and clip
    181     {
    182         SkRegion rgn;
    183         canvas->temporary_internal_getRgnClip(&rgn);
    184         setup_MC_state(&canvasState->mcState, canvas->getTotalMatrix(), rgn);
    185     }
    186 
    187     /*
    188      * decompose the layers
    189      *
    190      * storage is allocated on the stack for the first 3 layers. It is common in
    191      * some view systems (e.g. Android) that a few non-clipped layers are present
    192      * and we will not need to malloc any additional memory in those cases.
    193      */
    194     SkSWriter32<3*sizeof(SkCanvasLayerState)> layerWriter;
    195     int layerCount = 0;
    196     for (SkCanvas::LayerIter layer(canvas); !layer.done(); layer.next()) {
    197 
    198         // we currently only work for bitmap backed devices
    199         SkPixmap pmap;
    200         if (!layer.device()->accessPixels(&pmap) || 0 == pmap.width() || 0 == pmap.height()) {
    201             return nullptr;
    202         }
    203 
    204         SkCanvasLayerState* layerState =
    205                 (SkCanvasLayerState*) layerWriter.reserve(sizeof(SkCanvasLayerState));
    206         layerState->type = kRaster_CanvasBackend;
    207         layerState->x = layer.x();
    208         layerState->y = layer.y();
    209         layerState->width = pmap.width();
    210         layerState->height = pmap.height();
    211 
    212         switch (pmap.colorType()) {
    213             case kN32_SkColorType:
    214                 layerState->raster.config = kARGB_8888_RasterConfig;
    215                 break;
    216             case kRGB_565_SkColorType:
    217                 layerState->raster.config = kRGB_565_RasterConfig;
    218                 break;
    219             default:
    220                 return nullptr;
    221         }
    222         layerState->raster.rowBytes = pmap.rowBytes();
    223         layerState->raster.pixels = pmap.writable_addr();
    224 
    225         SkRegion rgn;
    226         layer.clip(&rgn);
    227         setup_MC_state(&layerState->mcState, layer.matrix(), rgn);
    228         layerCount++;
    229     }
    230 
    231     // allocate memory for the layers and then and copy them to the struct
    232     SkASSERT(layerWriter.bytesWritten() == layerCount * sizeof(SkCanvasLayerState));
    233     canvasState->layerCount = layerCount;
    234     canvasState->layers = (SkCanvasLayerState*) sk_malloc_throw(layerWriter.bytesWritten());
    235     layerWriter.flatten(canvasState->layers);
    236 
    237     return canvasState.release();
    238 }
    239 
    240 ////////////////////////////////////////////////////////////////////////////////
    241 
    242 static void setup_canvas_from_MC_state(const SkMCState& state, SkCanvas* canvas) {
    243     // reconstruct the matrix
    244     SkMatrix matrix;
    245     for (int i = 0; i < 9; i++) {
    246         matrix.set(i, state.matrix[i]);
    247     }
    248 
    249     // reconstruct the clip
    250     SkRegion clip;
    251     for (int i = 0; i < state.clipRectCount; ++i) {
    252         clip.op(SkIRect::MakeLTRB(state.clipRects[i].left,
    253                                   state.clipRects[i].top,
    254                                   state.clipRects[i].right,
    255                                   state.clipRects[i].bottom),
    256                 SkRegion::kUnion_Op);
    257     }
    258 
    259     canvas->setMatrix(matrix);
    260     canvas->clipRegion(clip, kReplace_SkClipOp);
    261 }
    262 
    263 static std::unique_ptr<SkCanvas>
    264 make_canvas_from_canvas_layer(const SkCanvasLayerState& layerState) {
    265     SkASSERT(kRaster_CanvasBackend == layerState.type);
    266 
    267     SkBitmap bitmap;
    268     SkColorType colorType =
    269         layerState.raster.config == kARGB_8888_RasterConfig ? kN32_SkColorType :
    270         layerState.raster.config == kRGB_565_RasterConfig ? kRGB_565_SkColorType :
    271         kUnknown_SkColorType;
    272 
    273     if (colorType == kUnknown_SkColorType) {
    274         return nullptr;
    275     }
    276 
    277     bitmap.installPixels(SkImageInfo::Make(layerState.width, layerState.height,
    278                                            colorType, kPremul_SkAlphaType),
    279                          layerState.raster.pixels, (size_t) layerState.raster.rowBytes);
    280 
    281     SkASSERT(!bitmap.empty());
    282     SkASSERT(!bitmap.isNull());
    283 
    284     std::unique_ptr<SkCanvas> canvas(new SkCanvas(bitmap));
    285 
    286     // setup the matrix and clip
    287     setup_canvas_from_MC_state(layerState.mcState, canvas.get());
    288 
    289     return canvas;
    290 }
    291 
    292 std::unique_ptr<SkCanvas> SkCanvasStateUtils::MakeFromCanvasState(const SkCanvasState* state) {
    293     SkASSERT(state);
    294     // Currently there is only one possible version.
    295     SkASSERT(SkCanvasState_v1::kVersion == state->version);
    296 
    297     const SkCanvasState_v1* state_v1 = static_cast<const SkCanvasState_v1*>(state);
    298 
    299     if (state_v1->layerCount < 1) {
    300         return nullptr;
    301     }
    302 
    303     std::unique_ptr<SkCanvasStack> canvas(new SkCanvasStack(state->width, state->height));
    304 
    305     // setup the matrix and clip on the n-way canvas
    306     setup_canvas_from_MC_state(state_v1->mcState, canvas.get());
    307 
    308     // Iterate over the layers and add them to the n-way canvas
    309     for (int i = state_v1->layerCount - 1; i >= 0; --i) {
    310         std::unique_ptr<SkCanvas> canvasLayer = make_canvas_from_canvas_layer(state_v1->layers[i]);
    311         if (!canvasLayer.get()) {
    312             return nullptr;
    313         }
    314         canvas->pushCanvas(std::move(canvasLayer), SkIPoint::Make(state_v1->layers[i].x,
    315                                                                   state_v1->layers[i].y));
    316     }
    317 
    318     return std::move(canvas);
    319 }
    320 
    321 ////////////////////////////////////////////////////////////////////////////////
    322 
    323 void SkCanvasStateUtils::ReleaseCanvasState(SkCanvasState* state) {
    324     SkASSERT(!state || SkCanvasState_v1::kVersion == state->version);
    325     // Upcast to the correct version of SkCanvasState. This avoids having a virtual destructor on
    326     // SkCanvasState. That would be strange since SkCanvasState has no other virtual functions, and
    327     // instead uses the field "version" to determine how to behave.
    328     delete static_cast<SkCanvasState_v1*>(state);
    329 }
    330