普通文本  |  412行  |  13.67 KB

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "skia/ext/lazy_pixel_ref_utils.h"

#include <algorithm>

#include "skia/ext/lazy_pixel_ref.h"
#include "third_party/skia/include/core/SkBitmapDevice.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkDraw.h"
#include "third_party/skia/include/core/SkPixelRef.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkShader.h"
#include "third_party/skia/src/core/SkRasterClip.h"

namespace skia {

namespace {

// URI label for a lazily decoded SkPixelRef.
const char kLabelLazyDecoded[] = "lazy";

class LazyPixelRefSet {
 public:
  LazyPixelRefSet(
      std::vector<LazyPixelRefUtils::PositionLazyPixelRef>* pixel_refs)
      : pixel_refs_(pixel_refs) {}

  void Add(SkPixelRef* pixel_ref, const SkRect& rect) {
    // Only save lazy pixel refs.
    if (pixel_ref->getURI() &&
        !strcmp(pixel_ref->getURI(), kLabelLazyDecoded)) {
      LazyPixelRefUtils::PositionLazyPixelRef position_pixel_ref;
      position_pixel_ref.lazy_pixel_ref =
          static_cast<skia::LazyPixelRef*>(pixel_ref);
      position_pixel_ref.pixel_ref_rect = rect;
      pixel_refs_->push_back(position_pixel_ref);
    }
  }

 private:
  std::vector<LazyPixelRefUtils::PositionLazyPixelRef>* pixel_refs_;
};

class GatherPixelRefDevice : public SkBitmapDevice {
 public:
  GatherPixelRefDevice(const SkBitmap& bm, LazyPixelRefSet* lazy_pixel_ref_set)
      : SkBitmapDevice(bm), lazy_pixel_ref_set_(lazy_pixel_ref_set) {}

  virtual void clear(SkColor color) SK_OVERRIDE {}
  virtual void writePixels(const SkBitmap& bitmap,
                           int x,
                           int y,
                           SkCanvas::Config8888 config8888) SK_OVERRIDE {}

  virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) SK_OVERRIDE {
    SkBitmap bitmap;
    if (GetBitmapFromPaint(paint, &bitmap)) {
      SkRect clip_rect = SkRect::Make(draw.fRC->getBounds());
      AddBitmap(bitmap, clip_rect);
    }
  }

  virtual void drawPoints(const SkDraw& draw,
                          SkCanvas::PointMode mode,
                          size_t count,
                          const SkPoint points[],
                          const SkPaint& paint) SK_OVERRIDE {
    SkBitmap bitmap;
    if (!GetBitmapFromPaint(paint, &bitmap))
      return;

    if (count == 0)
      return;

    SkPoint min_point = points[0];
    SkPoint max_point = points[0];
    for (size_t i = 1; i < count; ++i) {
      const SkPoint& point = points[i];
      min_point.set(std::min(min_point.x(), point.x()),
                    std::min(min_point.y(), point.y()));
      max_point.set(std::max(max_point.x(), point.x()),
                    std::max(max_point.y(), point.y()));
    }

    SkRect bounds = SkRect::MakeLTRB(
        min_point.x(), min_point.y(), max_point.x(), max_point.y());

    GatherPixelRefDevice::drawRect(draw, bounds, paint);
  }
  virtual void drawRect(const SkDraw& draw,
                        const SkRect& rect,
                        const SkPaint& paint) SK_OVERRIDE {
    SkBitmap bitmap;
    if (GetBitmapFromPaint(paint, &bitmap)) {
      SkRect mapped_rect;
      draw.fMatrix->mapRect(&mapped_rect, rect);
      mapped_rect.intersect(SkRect::Make(draw.fRC->getBounds()));
      AddBitmap(bitmap, mapped_rect);
    }
  }
  virtual void drawOval(const SkDraw& draw,
                        const SkRect& rect,
                        const SkPaint& paint) SK_OVERRIDE {
    GatherPixelRefDevice::drawRect(draw, rect, paint);
  }
  virtual void drawRRect(const SkDraw& draw,
                         const SkRRect& rect,
                         const SkPaint& paint) SK_OVERRIDE {
    GatherPixelRefDevice::drawRect(draw, rect.rect(), paint);
  }
  virtual void drawPath(const SkDraw& draw,
                        const SkPath& path,
                        const SkPaint& paint,
                        const SkMatrix* pre_path_matrix,
                        bool path_is_mutable) SK_OVERRIDE {
    SkBitmap bitmap;
    if (!GetBitmapFromPaint(paint, &bitmap))
      return;

    SkRect path_bounds = path.getBounds();
    SkRect final_rect;
    if (pre_path_matrix != NULL)
      pre_path_matrix->mapRect(&final_rect, path_bounds);
    else
      final_rect = path_bounds;

    GatherPixelRefDevice::drawRect(draw, final_rect, paint);
  }
  virtual void drawBitmap(const SkDraw& draw,
                          const SkBitmap& bitmap,
                          const SkMatrix& matrix,
                          const SkPaint& paint) SK_OVERRIDE {
    SkMatrix total_matrix;
    total_matrix.setConcat(*draw.fMatrix, matrix);

    SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
    SkRect mapped_rect;
    total_matrix.mapRect(&mapped_rect, bitmap_rect);
    AddBitmap(bitmap, mapped_rect);

    SkBitmap paint_bitmap;
    if (GetBitmapFromPaint(paint, &paint_bitmap))
      AddBitmap(paint_bitmap, mapped_rect);
  }
  virtual void drawBitmapRect(const SkDraw& draw,
                              const SkBitmap& bitmap,
                              const SkRect* src_or_null,
                              const SkRect& dst,
                              const SkPaint& paint,
                              SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE {
    SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
    SkMatrix matrix;
    matrix.setRectToRect(bitmap_rect, dst, SkMatrix::kFill_ScaleToFit);
    GatherPixelRefDevice::drawBitmap(draw, bitmap, matrix, paint);
  }
  virtual void drawSprite(const SkDraw& draw,
                          const SkBitmap& bitmap,
                          int x,
                          int y,
                          const SkPaint& paint) SK_OVERRIDE {
    // Sprites aren't affected by current matrix, so we can't reuse drawRect.
    SkMatrix matrix;
    matrix.setTranslate(x, y);

    SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
    SkRect mapped_rect;
    matrix.mapRect(&mapped_rect, bitmap_rect);

    AddBitmap(bitmap, mapped_rect);
    SkBitmap paint_bitmap;
    if (GetBitmapFromPaint(paint, &paint_bitmap))
      AddBitmap(paint_bitmap, mapped_rect);
  }
  virtual void drawText(const SkDraw& draw,
                        const void* text,
                        size_t len,
                        SkScalar x,
                        SkScalar y,
                        const SkPaint& paint) SK_OVERRIDE {
    SkBitmap bitmap;
    if (!GetBitmapFromPaint(paint, &bitmap))
      return;

    // Math is borrowed from SkBBoxRecord
    SkRect bounds;
    paint.measureText(text, len, &bounds);
    SkPaint::FontMetrics metrics;
    paint.getFontMetrics(&metrics);

    if (paint.isVerticalText()) {
      SkScalar h = bounds.fBottom - bounds.fTop;
      if (paint.getTextAlign() == SkPaint::kCenter_Align) {
        bounds.fTop -= h / 2;
        bounds.fBottom -= h / 2;
      }
      bounds.fBottom += metrics.fBottom;
      bounds.fTop += metrics.fTop;
    } else {
      SkScalar w = bounds.fRight - bounds.fLeft;
      if (paint.getTextAlign() == SkPaint::kCenter_Align) {
        bounds.fLeft -= w / 2;
        bounds.fRight -= w / 2;
      } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
        bounds.fLeft -= w;
        bounds.fRight -= w;
      }
      bounds.fTop = metrics.fTop;
      bounds.fBottom = metrics.fBottom;
    }

    SkScalar pad = (metrics.fBottom - metrics.fTop) / 2;
    bounds.fLeft -= pad;
    bounds.fRight += pad;
    bounds.fLeft += x;
    bounds.fRight += x;
    bounds.fTop += y;
    bounds.fBottom += y;

    GatherPixelRefDevice::drawRect(draw, bounds, paint);
  }
  virtual void drawPosText(const SkDraw& draw,
                           const void* text,
                           size_t len,
                           const SkScalar pos[],
                           SkScalar const_y,
                           int scalars_per_pos,
                           const SkPaint& paint) SK_OVERRIDE {
    SkBitmap bitmap;
    if (!GetBitmapFromPaint(paint, &bitmap))
      return;

    if (len == 0)
      return;

    // Similar to SkDraw asserts.
    SkASSERT(scalars_per_pos == 1 || scalars_per_pos == 2);

    SkPoint min_point;
    SkPoint max_point;
    if (scalars_per_pos == 1) {
      min_point.set(pos[0], const_y);
      max_point.set(pos[0], const_y);
    } else if (scalars_per_pos == 2) {
      min_point.set(pos[0], const_y + pos[1]);
      max_point.set(pos[0], const_y + pos[1]);
    }

    for (size_t i = 0; i < len; ++i) {
      SkScalar x = pos[i * scalars_per_pos];
      SkScalar y = const_y;
      if (scalars_per_pos == 2)
        y += pos[i * scalars_per_pos + 1];

      min_point.set(std::min(x, min_point.x()), std::min(y, min_point.y()));
      max_point.set(std::max(x, max_point.x()), std::max(y, max_point.y()));
    }

    SkRect bounds = SkRect::MakeLTRB(
        min_point.x(), min_point.y(), max_point.x(), max_point.y());

    // Math is borrowed from SkBBoxRecord
    SkPaint::FontMetrics metrics;
    paint.getFontMetrics(&metrics);

    bounds.fTop += metrics.fTop;
    bounds.fBottom += metrics.fBottom;

    SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
    bounds.fLeft += pad;
    bounds.fRight -= pad;

    GatherPixelRefDevice::drawRect(draw, bounds, paint);
  }
  virtual void drawTextOnPath(const SkDraw& draw,
                              const void* text,
                              size_t len,
                              const SkPath& path,
                              const SkMatrix* matrix,
                              const SkPaint& paint) SK_OVERRIDE {
    SkBitmap bitmap;
    if (!GetBitmapFromPaint(paint, &bitmap))
      return;

    // Math is borrowed from SkBBoxRecord
    SkRect bounds = path.getBounds();
    SkPaint::FontMetrics metrics;
    paint.getFontMetrics(&metrics);

    SkScalar pad = metrics.fTop;
    bounds.fLeft += pad;
    bounds.fRight -= pad;
    bounds.fTop += pad;
    bounds.fBottom -= pad;

    GatherPixelRefDevice::drawRect(draw, bounds, paint);
  }
  virtual void drawVertices(const SkDraw& draw,
                            SkCanvas::VertexMode,
                            int vertex_count,
                            const SkPoint verts[],
                            const SkPoint texs[],
                            const SkColor colors[],
                            SkXfermode* xmode,
                            const uint16_t indices[],
                            int index_count,
                            const SkPaint& paint) SK_OVERRIDE {
    GatherPixelRefDevice::drawPoints(
        draw, SkCanvas::kPolygon_PointMode, vertex_count, verts, paint);
  }
  virtual void drawDevice(const SkDraw&,
                          SkBaseDevice*,
                          int x,
                          int y,
                          const SkPaint&) SK_OVERRIDE {}

 protected:
  virtual bool onReadPixels(const SkBitmap& bitmap,
                            int x,
                            int y,
                            SkCanvas::Config8888 config8888) SK_OVERRIDE {
    return false;
  }

 private:
  LazyPixelRefSet* lazy_pixel_ref_set_;

  void AddBitmap(const SkBitmap& bm, const SkRect& rect) {
    SkRect canvas_rect = SkRect::MakeWH(width(), height());
    SkRect paint_rect = SkRect::MakeEmpty();
    paint_rect.intersect(rect, canvas_rect);
    lazy_pixel_ref_set_->Add(bm.pixelRef(), paint_rect);
  }

  bool GetBitmapFromPaint(const SkPaint& paint, SkBitmap* bm) {
    SkShader* shader = paint.getShader();
    if (shader) {
      // Check whether the shader is a gradient in order to prevent generation
      // of bitmaps from gradient shaders, which implement asABitmap.
      if (SkShader::kNone_GradientType == shader->asAGradient(NULL))
        return shader->asABitmap(bm, NULL, NULL);
    }
    return false;
  }
};

class NoSaveLayerCanvas : public SkCanvas {
 public:
  NoSaveLayerCanvas(SkBaseDevice* device) : INHERITED(device) {}

  // Turn saveLayer() into save() for speed, should not affect correctness.
  virtual int saveLayer(const SkRect* bounds,
                        const SkPaint* paint,
                        SaveFlags flags) SK_OVERRIDE {

    // Like SkPictureRecord, we don't want to create layers, but we do need
    // to respect the save and (possibly) its rect-clip.
    int count = this->INHERITED::save(flags);
    if (bounds) {
      this->INHERITED::clipRectBounds(bounds, flags, NULL);
    }
    return count;
  }

  // Disable aa for speed.
  virtual bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA)
      SK_OVERRIDE {
    return this->INHERITED::clipRect(rect, op, false);
  }

  virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA)
      SK_OVERRIDE {
    return this->updateClipConservativelyUsingBounds(
        path.getBounds(), op, path.isInverseFillType());
  }
  virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA)
      SK_OVERRIDE {
    return this->updateClipConservativelyUsingBounds(
        rrect.getBounds(), op, false);
  }

 private:
  typedef SkCanvas INHERITED;
};

}  // namespace

void LazyPixelRefUtils::GatherPixelRefs(
    SkPicture* picture,
    std::vector<PositionLazyPixelRef>* lazy_pixel_refs) {
  lazy_pixel_refs->clear();
  LazyPixelRefSet pixel_ref_set(lazy_pixel_refs);

  SkBitmap empty_bitmap;
  empty_bitmap.setConfig(
      SkBitmap::kNo_Config, picture->width(), picture->height());

  GatherPixelRefDevice device(empty_bitmap, &pixel_ref_set);
  NoSaveLayerCanvas canvas(&device);

  canvas.clipRect(SkRect::MakeWH(picture->width(), picture->height()),
                  SkRegion::kIntersect_Op,
                  false);
  canvas.drawPicture(*picture);
}

}  // namespace skia