普通文本  |  428行  |  14.39 KB

// Copyright (c) 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 "base/debug/trace_event.h"
#include "base/logging.h"
#include "skia/ext/analysis_canvas.h"
#include "third_party/skia/include/core/SkDevice.h"
#include "third_party/skia/include/core/SkDraw.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkShader.h"
#include "third_party/skia/src/core/SkRasterClip.h"
#include "ui/gfx/rect_conversions.h"

namespace {

const int kNoLayer = -1;

bool IsSolidColorPaint(const SkPaint& paint) {
  SkXfermode::Mode xfermode;

  // getXfermode can return a NULL, but that is handled
  // gracefully by AsMode (NULL turns into kSrcOver mode).
  SkXfermode::AsMode(paint.getXfermode(), &xfermode);

  // Paint is solid color if the following holds:
  // - Alpha is 1.0, style is fill, and there are no special effects
  // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent
  //   to kSrc if source alpha is 1.0, which is already checked).
  return (paint.getAlpha() == 255 &&
          !paint.getShader() &&
          !paint.getLooper() &&
          !paint.getMaskFilter() &&
          !paint.getColorFilter() &&
          paint.getStyle() == SkPaint::kFill_Style &&
          (xfermode == SkXfermode::kSrc_Mode ||
           xfermode == SkXfermode::kSrcOver_Mode));
}

bool IsFullQuad(const SkDraw& draw,
                const SkRect& canvas_rect,
                const SkRect& drawn_rect) {

  // If the transform results in a non-axis aligned
  // rect, then be conservative and return false.
  if (!draw.fMatrix->rectStaysRect())
    return false;

  SkRect draw_bitmap_rect;
  draw.fBitmap->getBounds(&draw_bitmap_rect);
  SkRect clip_rect = SkRect::Make(draw.fRC->getBounds());
  SkRect device_rect;
  draw.fMatrix->mapRect(&device_rect, drawn_rect);

  // The drawn rect covers the full canvas, if the following conditions hold:
  // - Clip rect is an actual rectangle.
  // - The rect we're drawing (post-transform) contains the clip rect.
  //   That is, all of clip rect will be colored by the rect.
  // - Clip rect contains the canvas rect.
  //   That is, we're not clipping to a portion of this canvas.
  // - The bitmap into which the draw call happens is at least as
  //   big as the canvas rect
  return draw.fRC->isRect() &&
         device_rect.contains(clip_rect) &&
         clip_rect.contains(canvas_rect) &&
         draw_bitmap_rect.contains(canvas_rect);
}

} // namespace

namespace skia {

AnalysisDevice::AnalysisDevice(const SkBitmap& bitmap)
    : INHERITED(bitmap),
      is_forced_not_solid_(false),
      is_forced_not_transparent_(false),
      is_solid_color_(true),
      is_transparent_(true),
      has_text_(false) {}

AnalysisDevice::~AnalysisDevice() {}

bool AnalysisDevice::GetColorIfSolid(SkColor* color) const {
  if (is_transparent_) {
    *color = SK_ColorTRANSPARENT;
    return true;
  }
  if (is_solid_color_) {
    *color = color_;
    return true;
  }
  return false;
}

bool AnalysisDevice::HasText() const {
  return has_text_;
}

void AnalysisDevice::SetForceNotSolid(bool flag) {
  is_forced_not_solid_ = flag;
  if (is_forced_not_solid_)
    is_solid_color_ = false;
}

void AnalysisDevice::SetForceNotTransparent(bool flag) {
  is_forced_not_transparent_ = flag;
  if (is_forced_not_transparent_)
    is_transparent_ = false;
}

void AnalysisDevice::clear(SkColor color) {
  is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0);
  has_text_ = false;

  if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
    is_solid_color_ = true;
    color_ = color;
  } else {
    is_solid_color_ = false;
  }
}

void AnalysisDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
}

void AnalysisDevice::drawPoints(const SkDraw& draw,
                                SkCanvas::PointMode mode,
                                size_t count,
                                const SkPoint points[],
                                const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
}

void AnalysisDevice::drawRect(const SkDraw& draw,
                              const SkRect& rect,
                              const SkPaint& paint) {
  bool does_cover_canvas =
      IsFullQuad(draw, SkRect::MakeWH(width(), height()), rect);

  SkXfermode::Mode xfermode;
  SkXfermode::AsMode(paint.getXfermode(), &xfermode);

  // This canvas will become transparent if the following holds:
  // - The quad is a full tile quad
  // - We're not in "forced not transparent" mode
  // - Transfer mode is clear (0 color, 0 alpha)
  //
  // If the paint alpha is not 0, or if the transfrer mode is
  // not src, then this canvas will not be transparent.
  //
  // In all other cases, we keep the current transparent value
  if (does_cover_canvas &&
      !is_forced_not_transparent_ &&
      xfermode == SkXfermode::kClear_Mode) {
    is_transparent_ = true;
    has_text_ = false;
  } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
    is_transparent_ = false;
  }

  // This bitmap is solid if and only if the following holds.
  // Note that this might be overly conservative:
  // - We're not in "forced not solid" mode
  // - Paint is solid color
  // - The quad is a full tile quad
  if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
    is_solid_color_ = true;
    color_ = paint.getColor();
    has_text_ = false;
  } else {
    is_solid_color_ = false;
  }
}

void AnalysisDevice::drawOval(const SkDraw& draw,
                              const SkRect& oval,
                              const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
}

void AnalysisDevice::drawRRect(const SkDraw& draw,
                               const SkRRect& rr,
                               const SkPaint& paint) {
  // This should add the SkRRect to an SkPath, and call
  // drawPath, but since drawPath ignores the SkPath, just
  // do the same work here.
  is_solid_color_ = false;
  is_transparent_ = false;
}

void AnalysisDevice::drawPath(const SkDraw& draw,
                              const SkPath& path,
                              const SkPaint& paint,
                              const SkMatrix* pre_path_matrix,
                              bool path_is_mutable) {
  is_solid_color_ = false;
  is_transparent_ = false;
}

void AnalysisDevice::drawBitmap(const SkDraw& draw,
                                const SkBitmap& bitmap,
                                const SkMatrix& matrix,
                                const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
}

void AnalysisDevice::drawSprite(const SkDraw& draw,
                                const SkBitmap& bitmap,
                                int x,
                                int y,
                                const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
}

void AnalysisDevice::drawBitmapRect(const SkDraw& draw,
                                    const SkBitmap& bitmap,
                                    const SkRect* src_or_null,
                                    const SkRect& dst,
                                    const SkPaint& paint,
                                    SkCanvas::DrawBitmapRectFlags flags) {
  // Call drawRect to determine transparency,
  // but reset solid color to false.
  drawRect(draw, dst, paint);
  is_solid_color_ = false;
}

void AnalysisDevice::drawText(const SkDraw& draw,
                              const void* text,
                              size_t len,
                              SkScalar x,
                              SkScalar y,
                              const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
  has_text_ = true;
}

void AnalysisDevice::drawPosText(const SkDraw& draw,
                                 const void* text,
                                 size_t len,
                                 const SkScalar pos[],
                                 SkScalar const_y,
                                 int scalars_per_pos,
                                 const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
  has_text_ = true;
}

void AnalysisDevice::drawTextOnPath(const SkDraw& draw,
                                    const void* text,
                                    size_t len,
                                    const SkPath& path,
                                    const SkMatrix* matrix,
                                    const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
  has_text_ = true;
}

#ifdef SK_BUILD_FOR_ANDROID
void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw,
                                       const void* text,
                                       size_t len,
                                       const SkPoint pos[],
                                       const SkPaint& paint,
                                       const SkPath& path,
                                       const SkMatrix* matrix) {
  is_solid_color_ = false;
  is_transparent_ = false;
  has_text_ = true;
}
#endif

void AnalysisDevice::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) {
  is_solid_color_ = false;
  is_transparent_ = false;
}

void AnalysisDevice::drawDevice(const SkDraw& draw,
                                SkBaseDevice* device,
                                int x,
                                int y,
                                const SkPaint& paint) {
  is_solid_color_ = false;
  is_transparent_ = false;
}

AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device)
    : INHERITED(device),
      saved_stack_size_(0),
      force_not_solid_stack_level_(kNoLayer),
      force_not_transparent_stack_level_(kNoLayer) {}

AnalysisCanvas::~AnalysisCanvas() {}

bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
  return (static_cast<AnalysisDevice*>(getDevice()))->GetColorIfSolid(color);
}

bool AnalysisCanvas::HasText() const {
  return (static_cast<AnalysisDevice*>(getDevice()))->HasText();
}

bool AnalysisCanvas::abortDrawing() {
  // Early out as soon as we have detected that the tile has text.
  return HasText();
}

bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool do_aa) {
  return INHERITED::clipRect(rect, op, do_aa);
}

bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool do_aa) {
  // clipPaths can make our calls to IsFullQuad invalid (ie have false
  // positives). As a precaution, force the setting to be non-solid
  // and non-transparent until we pop this
  if (force_not_solid_stack_level_ == kNoLayer) {
    force_not_solid_stack_level_ = saved_stack_size_;
    (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
  }
  if (force_not_transparent_stack_level_ == kNoLayer) {
    force_not_transparent_stack_level_ = saved_stack_size_;
    (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
  }

  return INHERITED::clipRect(path.getBounds(), op, do_aa);
}

bool AnalysisCanvas::clipRRect(const SkRRect& rrect,
                               SkRegion::Op op,
                               bool do_aa) {
  // clipRRect can make our calls to IsFullQuad invalid (ie have false
  // positives). As a precaution, force the setting to be non-solid
  // and non-transparent until we pop this
  if (force_not_solid_stack_level_ == kNoLayer) {
    force_not_solid_stack_level_ = saved_stack_size_;
    (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
  }
  if (force_not_transparent_stack_level_ == kNoLayer) {
    force_not_transparent_stack_level_ = saved_stack_size_;
    (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
  }

  return INHERITED::clipRect(rrect.getBounds(), op, do_aa);
}

int AnalysisCanvas::save(SkCanvas::SaveFlags flags) {
  ++saved_stack_size_;
  return INHERITED::save(flags);
}

int AnalysisCanvas::saveLayer(const SkRect* bounds,
                              const SkPaint* paint,
                              SkCanvas::SaveFlags flags) {
  ++saved_stack_size_;

  // If after we draw to the saved layer, we have to blend with the current
  // layer, then we can conservatively say that the canvas will not be of
  // solid color.
  if ((paint && !IsSolidColorPaint(*paint)) ||
      (bounds && !bounds->contains(SkRect::MakeWH(getDevice()->width(),
                                                  getDevice()->height())))) {
    if (force_not_solid_stack_level_ == kNoLayer) {
      force_not_solid_stack_level_ = saved_stack_size_;
      (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
    }
  }

  // If after we draw to the save layer, we have to blend with the current
  // layer using any part of the current layer's alpha, then we can
  // conservatively say that the canvas will not be transparent.
  SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
  if (paint)
    SkXfermode::AsMode(paint->getXfermode(), &xfermode);
  if (xfermode != SkXfermode::kSrc_Mode) {
    if (force_not_transparent_stack_level_ == kNoLayer) {
      force_not_transparent_stack_level_ = saved_stack_size_;
      (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
    }
  }

  // Actually saving a layer here could cause a new bitmap to be created
  // and real rendering to occur.
  int count = INHERITED::save(flags);
  if (bounds) {
    INHERITED::clipRectBounds(bounds, flags, NULL);
  }
  return count;
}

void AnalysisCanvas::restore() {
  INHERITED::restore();

  DCHECK(saved_stack_size_);
  if (saved_stack_size_) {
    --saved_stack_size_;
    if (saved_stack_size_ < force_not_solid_stack_level_) {
      (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(false);
      force_not_solid_stack_level_ = kNoLayer;
    }
    if (saved_stack_size_ < force_not_transparent_stack_level_) {
      (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(
          false);
      force_not_transparent_stack_level_ = kNoLayer;
    }
  }
}

}  // namespace skia