Javascript  |  182行  |  5.34 KB

// Copyright (c) 2012 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.

'use strict';

base.require('base.range');
base.require('base.events');

base.exportTo('ui', function() {
  // FIXME(pdr): Replace this padding with just what's necessary for
  //             drawing borders / highlights.
  //             https://code.google.com/p/trace-viewer/issues/detail?id=228
  var DEFAULT_PAD_PERCENTAGE = 0.75;

  function QuadViewViewport(worldRect,
                            opt_quad_stack_scale,
                            opt_padding,
                            opt_devicePixelRatio) {
    base.EventTarget.call(this);
    if (!worldRect)
      throw new Error('Cannot initialize a viewport with an empty worldRect');

    // Physical pixels / device-independent pixels;
    // 1 is normal; higher for eg Retina
    this.devicePixelRatio =
        opt_devicePixelRatio || window.devicePixelRatio || 1;

    this.layoutRect_ = undefined;
    this.setWorldRect_(worldRect, opt_padding);

    var scale;
    if (opt_quad_stack_scale) {
      scale = opt_quad_stack_scale;
    } else {
      scale = 0.125;
      if (this.devicePixelRatio > 1)
        scale = scale * this.devicePixelRatio;
    }
    this.worldPixelsPerDevicePixel_ = scale;

    this.updateScale_();
  }

  QuadViewViewport.prototype = {

    __proto__: base.EventTarget.prototype,

    // The pixels in the original, traced browser are
    // represented in a canvas 'world', scaled by a
    // this 'scale' value.
    set scale(scale) {
      this.worldPixelsPerDevicePixel_ = scale;
      this.updateScale_();
      this.didChange_();
    },

    get scale() {
      return this.worldPixelsPerDevicePixel_;
    },

    get worldRect() {
      return this.worldRect_;
    },

    get unpaddedWorldRect() {
      return this.unpaddedWorldRect_;
    },

    updateBoxSize: function(canvas) {
      var resizedCanvas = false;
      if (canvas.width !== this.worldWidthInDevicePixels_) {
        canvas.width = this.worldWidthInDevicePixels_ * ui.RASTER_SCALE;
        canvas.style.width = this.layoutRect_.width + 'px';
        resizedCanvas = true;
      }
      if (canvas.height !== this.worldHeightInDevicePixels_) {
        canvas.height = this.worldHeightInDevicePixels_ * ui.RASTER_SCALE;
        canvas.style.height = this.layoutRect_.height + 'px';
        resizedCanvas = true;
      }
      return resizedCanvas;
    },

    layoutPixelsToWorldPixels: function(v) {
      var tmp = this.layoutPixelsToDevicePixels(v);
      return this.devicePixelsToWorldPixels(tmp);
    },

    layoutPixelsToDevicePixels: function(v) {
      var res = vec2.create();
      return vec2.scale(res, v, this.devicePixelRatio);
    },

    devicePixelsToWorldPixels: function(v) {
      var res = vec2.create();
      vec2.transformMat2d(res, v, this.transformDevicePixelsToWorld_);
      return res;
    },

    getWorldToDevicePixelsTransform: function() {
      return this.transformWorldToDevicePixels_;
    },

    getDeviceLineWidthAssumingTransformIsApplied: function(
        desiredDeviceLineWidth) {
      return desiredDeviceLineWidth / this.worldPixelsPerDevicePixel_;
    },

    applyTransformToContext: function(ctx) {
      var transform = this.transformWorldToDevicePixels_;
      ctx.transform(transform[0], transform[1], transform[2],
                    transform[3], transform[4], transform[5]);
    },

    forceRedrawAll: function() {
      this.didChange_();
    },

    //-------------------------------------------

    updateScale_: function() {
      this.worldWidthInDevicePixels_ =
          this.worldRect_.width * this.worldPixelsPerDevicePixel_;
      this.worldHeightInDevicePixels_ =
          this.worldRect_.height * this.worldPixelsPerDevicePixel_;

      this.updateLayoutRect_();

      this.transformWorldToDevicePixels_ = mat2d.create();
      this.transformDevicePixelsToWorld_ = mat2d.create();
      this.updateTransform_();
    },

    /** Adjust the scaled world box for Retina-like displays */
    updateLayoutRect_: function() {
      var devicePixelsPerLayoutPixel =
          this.worldPixelsPerDevicePixel_ / this.devicePixelRatio;
      this.layoutRect_ = this.worldRect.scale(devicePixelsPerLayoutPixel);
    },

    setWorldRect_: function(worldRect, opt_padding) {
      var worldPad;
      var padding;
      if (opt_padding !== undefined) {
        padding = opt_padding;
      } else {
        padding = DEFAULT_PAD_PERCENTAGE;
      }
      worldPad = Math.min(worldRect.width,
                          worldRect.height) * padding;

      this.unpaddedWorldRect_ = worldRect;
      this.worldRect_ = worldRect.clone().enlarge(worldPad);
    },

    updateTransform_: function() {
      if (!this.transformWorldToDevicePixels_)
        return;

      mat2d.identity(this.transformWorldToDevicePixels_);
      mat2d.translateXY(
          this.transformWorldToDevicePixels_,
          -this.worldRect_.x, -this.worldRect_.y);
      mat2d.scaleXY(this.transformWorldToDevicePixels_,
          this.worldPixelsPerDevicePixel_,
          this.worldPixelsPerDevicePixel_);

      mat2d.invert(this.transformDevicePixelsToWorld_,
                   this.transformWorldToDevicePixels_);
    },

    didChange_: function() {
      base.dispatchSimpleEvent(this, 'change', false, false);
    }
  };

  return {
    QuadViewViewport: QuadViewViewport
  };
});