<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "brpc/circuit_breaker.h"

#include <cmath>
#include <gflags/gflags.h>

#include "brpc/errno.pb.h"
#include "butil/logging.h"
#include "butil/strings/string_number_conversions.h"
#include "butil/strings/string_split.h"
#include "butil/time.h"

namespace brpc {

DEFINE_int32(circuit_breaker_short_window_size, 1500,
    "Short window sample size.");
DEFINE_int32(circuit_breaker_long_window_size, 3000,
    "Long window sample size.");
DEFINE_int32(circuit_breaker_short_window_error_percent, 10,
    "The maximum error rate allowed by the short window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_long_window_error_percent, 5,
    "The maximum error rate allowed by the long window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_min_error_cost_us, 500,
    "The minimum error_cost, when the ema of error cost is less than this "
    "value, it will be set to zero.");
DEFINE_int32(circuit_breaker_max_failed_latency_mutiple, 2,
    "The maximum multiple of the latency of the failed request relative to "
    "the average latency of the success requests.");
DEFINE_int32(circuit_breaker_min_isolation_duration_ms, 100,
    "Minimum isolation duration in milliseconds");
DEFINE_int32(circuit_breaker_max_isolation_duration_ms, 30000,
    "Maximum isolation duration in milliseconds");
DEFINE_double(circuit_breaker_epsilon_value, 0.02,
    "ema_alpha = 1 - std::pow(epsilon, 1.0 / window_size)");

namespace {
// EPSILON is used to generate the smoothing coefficient when calculating EMA.
// The larger the EPSILON, the larger the smoothing coefficient, which means
// that the proportion of early data is larger.
// smooth = pow(EPSILON, 1 / window_size),
// eg: when window_size = 100,
// EPSILON = 0.1, smooth = 0.9772
// EPSILON = 0.3, smooth = 0.9880
// when window_size = 1000,
// EPSILON = 0.1, smooth = 0.9977
// EPSILON = 0.3, smooth = 0.9987

#define EPSILON (FLAGS_circuit_breaker_epsilon_value)

}  // namespace

CircuitBreaker::EmaErrorRecorder::EmaErrorRecorder(int window_size,
                                                   int max_error_percent)
    : _window_size(window_size)
    , _max_error_percent(max_error_percent)
    , _smooth(std::pow(EPSILON, 1.0/window_size))
    , _sample_count_when_initializing(0)
    , _error_count_when_initializing(0)
    , _ema_error_cost(0)
    , _ema_latency(0) {
}

bool CircuitBreaker::EmaErrorRecorder::OnCallEnd(int error_code,
                                                 int64_t latency) {
    int64_t ema_latency = 0;
    bool healthy = false;
    if (error_code == 0) {
        ema_latency = UpdateLatency(latency);
        healthy = UpdateErrorCost(0, ema_latency);
    } else {
        ema_latency = _ema_latency.load(butil::memory_order_relaxed);
        healthy = UpdateErrorCost(latency, ema_latency);
    }

    // When the window is initializing, use error_rate to determine
    // if it needs to be isolated.
    if (_sample_count_when_initializing.load(butil::memory_order_relaxed) < _window_size &&
        _sample_count_when_initializing.fetch_add(1, butil::memory_order_relaxed) < _window_size) {
        if (error_code != 0) {
            const int32_t error_count =
                _error_count_when_initializing.fetch_add(1, butil::memory_order_relaxed);
            return error_count < _window_size * _max_error_percent / 100;
        }
        // Because once OnCallEnd returned false, the node will be ioslated soon,
        // so when error_code=0, we no longer check the error count.
        return true;
    }

    return healthy;
}

void CircuitBreaker::EmaErrorRecorder::Reset() {
    if (_sample_count_when_initializing.load(butil::memory_order_relaxed) < _window_size) {
        _sample_count_when_initializing.store(0, butil::memory_order_relaxed);
        _error_count_when_initializing.store(0, butil::memory_order_relaxed);
        _ema_latency.store(0, butil::memory_order_relaxed);
    }
    _ema_error_cost.store(0, butil::memory_order_relaxed);
}

int64_t CircuitBreaker::EmaErrorRecorder::UpdateLatency(int64_t latency) {
    int64_t ema_latency = _ema_latency.load(butil::memory_order_relaxed);
    do {
        int64_t next_ema_latency = 0;
        if (0 == ema_latency) {
            next_ema_latency = latency;
        } else {
            next_ema_latency = ema_latency * _smooth + latency * (1 - _smooth);
        }
        if (_ema_latency.compare_exchange_weak(ema_latency, next_ema_latency)) {
            return next_ema_latency;
        }
    } while(true);
}

bool CircuitBreaker::EmaErrorRecorder::UpdateErrorCost(int64_t error_cost,
                                                       int64_t ema_latency) {
    const int max_mutiple = FLAGS_circuit_breaker_max_failed_latency_mutiple;
    if (ema_latency != 0) {
        error_cost = std::min(ema_latency * max_mutiple, error_cost);
    }
    //Errorous response
    if (error_cost != 0) {
        int64_t ema_error_cost =
            _ema_error_cost.fetch_add(error_cost, butil::memory_order_relaxed);
        ema_error_cost += error_cost;
        const int64_t max_error_cost =
            ema_latency * _window_size * (_max_error_percent / 100.0) * (1.0 + EPSILON);
        return ema_error_cost <= max_error_cost;
    }

    //Ordinary response
    int64_t ema_error_cost = _ema_error_cost.load(butil::memory_order_relaxed);
    do {
        if (ema_error_cost == 0) {
            break;
        } else if (ema_error_cost < FLAGS_circuit_breaker_min_error_cost_us) {
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, 0, butil::memory_order_relaxed)) {
                break;
            }
        } else {
            int64_t next_ema_error_cost = ema_error_cost * _smooth;
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, next_ema_error_cost)) {
                break;
            }
        }
    } while (true);
    return true;
}

CircuitBreaker::CircuitBreaker()
    : _long_window(FLAGS_circuit_breaker_long_window_size,
                   FLAGS_circuit_breaker_long_window_error_percent)
    , _short_window(FLAGS_circuit_breaker_short_window_size,
                    FLAGS_circuit_breaker_short_window_error_percent)
    , _last_reset_time_ms(0)
    , _isolation_duration_ms(FLAGS_circuit_breaker_min_isolation_duration_ms)
    , _isolated_times(0)
    , _broken(false) {
}

bool CircuitBreaker::OnCallEnd(int error_code, int64_t latency) {
    // If the server has reached its maximum concurrency, it will return
    // ELIMIT directly when a new request arrives. This usually means that
    // the entire downstream cluster is overloaded. If we isolate nodes at
    // this time, may increase the pressure on downstream. On the other hand,
    // since the latency corresponding to ELIMIT is usually very small, we
    // cannot handle it as a successful request.Here we simply ignore the requests
    // that returns ELIMIT.
    if (error_code == ELIMIT) {
      return true;
    }
    if (_broken.load(butil::memory_order_relaxed)) {
        return false;
    }
    if (_long_window.OnCallEnd(error_code, latency) &&
        _short_window.OnCallEnd(error_code, latency)) {
        return true;
    }
    MarkAsBroken();
    return false;
}

void CircuitBreaker::Reset() {
    _long_window.Reset();
    _short_window.Reset();
    _last_reset_time_ms = butil::cpuwide_time_ms();
    _broken.store(false, butil::memory_order_release);
}

void CircuitBreaker::MarkAsBroken() {
    if (!_broken.exchange(true, butil::memory_order_acquire)) {
        _isolated_times.fetch_add(1, butil::memory_order_relaxed);
        UpdateIsolationDuration();
    }
}

void CircuitBreaker::UpdateIsolationDuration() {
    int64_t now_time_ms = butil::cpuwide_time_ms();
    int isolation_duration_ms = _isolation_duration_ms.load(butil::memory_order_relaxed);
    const int max_isolation_duration_ms =
        FLAGS_circuit_breaker_max_isolation_duration_ms;
    const int min_isolation_duration_ms =
        FLAGS_circuit_breaker_min_isolation_duration_ms;
    if (now_time_ms - _last_reset_time_ms < max_isolation_duration_ms) {
        isolation_duration_ms =
            std::min(isolation_duration_ms * 2, max_isolation_duration_ms);
    } else {
        isolation_duration_ms = min_isolation_duration_ms;
    }
    _isolation_duration_ms.store(isolation_duration_ms, butil::memory_order_relaxed);
}

}  // namespace brpc
<commit_msg>adjust indent<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "brpc/circuit_breaker.h"

#include <cmath>
#include <gflags/gflags.h>

#include "brpc/errno.pb.h"
#include "butil/logging.h"
#include "butil/strings/string_number_conversions.h"
#include "butil/strings/string_split.h"
#include "butil/time.h"

namespace brpc {

DEFINE_int32(circuit_breaker_short_window_size, 1500,
    "Short window sample size.");
DEFINE_int32(circuit_breaker_long_window_size, 3000,
    "Long window sample size.");
DEFINE_int32(circuit_breaker_short_window_error_percent, 10,
    "The maximum error rate allowed by the short window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_long_window_error_percent, 5,
    "The maximum error rate allowed by the long window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_min_error_cost_us, 500,
    "The minimum error_cost, when the ema of error cost is less than this "
    "value, it will be set to zero.");
DEFINE_int32(circuit_breaker_max_failed_latency_mutiple, 2,
    "The maximum multiple of the latency of the failed request relative to "
    "the average latency of the success requests.");
DEFINE_int32(circuit_breaker_min_isolation_duration_ms, 100,
    "Minimum isolation duration in milliseconds");
DEFINE_int32(circuit_breaker_max_isolation_duration_ms, 30000,
    "Maximum isolation duration in milliseconds");
DEFINE_double(circuit_breaker_epsilon_value, 0.02,
    "ema_alpha = 1 - std::pow(epsilon, 1.0 / window_size)");

namespace {
// EPSILON is used to generate the smoothing coefficient when calculating EMA.
// The larger the EPSILON, the larger the smoothing coefficient, which means
// that the proportion of early data is larger.
// smooth = pow(EPSILON, 1 / window_size),
// eg: when window_size = 100,
// EPSILON = 0.1, smooth = 0.9772
// EPSILON = 0.3, smooth = 0.9880
// when window_size = 1000,
// EPSILON = 0.1, smooth = 0.9977
// EPSILON = 0.3, smooth = 0.9987

#define EPSILON (FLAGS_circuit_breaker_epsilon_value)

}  // namespace

CircuitBreaker::EmaErrorRecorder::EmaErrorRecorder(int window_size,
                                                   int max_error_percent)
    : _window_size(window_size)
    , _max_error_percent(max_error_percent)
    , _smooth(std::pow(EPSILON, 1.0/window_size))
    , _sample_count_when_initializing(0)
    , _error_count_when_initializing(0)
    , _ema_error_cost(0)
    , _ema_latency(0) {
}

bool CircuitBreaker::EmaErrorRecorder::OnCallEnd(int error_code,
                                                 int64_t latency) {
    int64_t ema_latency = 0;
    bool healthy = false;
    if (error_code == 0) {
        ema_latency = UpdateLatency(latency);
        healthy = UpdateErrorCost(0, ema_latency);
    } else {
        ema_latency = _ema_latency.load(butil::memory_order_relaxed);
        healthy = UpdateErrorCost(latency, ema_latency);
    }

    // When the window is initializing, use error_rate to determine
    // if it needs to be isolated.
    if (_sample_count_when_initializing.load(butil::memory_order_relaxed) < _window_size &&
        _sample_count_when_initializing.fetch_add(1, butil::memory_order_relaxed) < _window_size) {
        if (error_code != 0) {
            const int32_t error_count =
                _error_count_when_initializing.fetch_add(1, butil::memory_order_relaxed);
            return error_count < _window_size * _max_error_percent / 100;
        }
        // Because once OnCallEnd returned false, the node will be ioslated soon,
        // so when error_code=0, we no longer check the error count.
        return true;
    }

    return healthy;
}

void CircuitBreaker::EmaErrorRecorder::Reset() {
    if (_sample_count_when_initializing.load(butil::memory_order_relaxed) < _window_size) {
        _sample_count_when_initializing.store(0, butil::memory_order_relaxed);
        _error_count_when_initializing.store(0, butil::memory_order_relaxed);
        _ema_latency.store(0, butil::memory_order_relaxed);
    }
    _ema_error_cost.store(0, butil::memory_order_relaxed);
}

int64_t CircuitBreaker::EmaErrorRecorder::UpdateLatency(int64_t latency) {
    int64_t ema_latency = _ema_latency.load(butil::memory_order_relaxed);
    do {
        int64_t next_ema_latency = 0;
        if (0 == ema_latency) {
            next_ema_latency = latency;
        } else {
            next_ema_latency = ema_latency * _smooth + latency * (1 - _smooth);
        }
        if (_ema_latency.compare_exchange_weak(ema_latency, next_ema_latency)) {
            return next_ema_latency;
        }
    } while(true);
}

bool CircuitBreaker::EmaErrorRecorder::UpdateErrorCost(int64_t error_cost,
                                                       int64_t ema_latency) {
    const int max_mutiple = FLAGS_circuit_breaker_max_failed_latency_mutiple;
    if (ema_latency != 0) {
        error_cost = std::min(ema_latency * max_mutiple, error_cost);
    }
    //Errorous response
    if (error_cost != 0) {
        int64_t ema_error_cost =
            _ema_error_cost.fetch_add(error_cost, butil::memory_order_relaxed);
        ema_error_cost += error_cost;
        const int64_t max_error_cost =
            ema_latency * _window_size * (_max_error_percent / 100.0) * (1.0 + EPSILON);
        return ema_error_cost <= max_error_cost;
    }

    //Ordinary response
    int64_t ema_error_cost = _ema_error_cost.load(butil::memory_order_relaxed);
    do {
        if (ema_error_cost == 0) {
            break;
        } else if (ema_error_cost < FLAGS_circuit_breaker_min_error_cost_us) {
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, 0, butil::memory_order_relaxed)) {
                break;
            }
        } else {
            int64_t next_ema_error_cost = ema_error_cost * _smooth;
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, next_ema_error_cost)) {
                break;
            }
        }
    } while (true);
    return true;
}

CircuitBreaker::CircuitBreaker()
    : _long_window(FLAGS_circuit_breaker_long_window_size,
                   FLAGS_circuit_breaker_long_window_error_percent)
    , _short_window(FLAGS_circuit_breaker_short_window_size,
                    FLAGS_circuit_breaker_short_window_error_percent)
    , _last_reset_time_ms(0)
    , _isolation_duration_ms(FLAGS_circuit_breaker_min_isolation_duration_ms)
    , _isolated_times(0)
    , _broken(false) {
}

bool CircuitBreaker::OnCallEnd(int error_code, int64_t latency) {
    // If the server has reached its maximum concurrency, it will return
    // ELIMIT directly when a new request arrives. This usually means that
    // the entire downstream cluster is overloaded. If we isolate nodes at
    // this time, may increase the pressure on downstream. On the other hand,
    // since the latency corresponding to ELIMIT is usually very small, we
    // cannot handle it as a successful request.Here we simply ignore the requests
    // that returns ELIMIT.
    if (error_code == ELIMIT) {
        return true;
    }
    if (_broken.load(butil::memory_order_relaxed)) {
        return false;
    }
    if (_long_window.OnCallEnd(error_code, latency) &&
        _short_window.OnCallEnd(error_code, latency)) {
        return true;
    }
    MarkAsBroken();
    return false;
}

void CircuitBreaker::Reset() {
    _long_window.Reset();
    _short_window.Reset();
    _last_reset_time_ms = butil::cpuwide_time_ms();
    _broken.store(false, butil::memory_order_release);
}

void CircuitBreaker::MarkAsBroken() {
    if (!_broken.exchange(true, butil::memory_order_acquire)) {
        _isolated_times.fetch_add(1, butil::memory_order_relaxed);
        UpdateIsolationDuration();
    }
}

void CircuitBreaker::UpdateIsolationDuration() {
    int64_t now_time_ms = butil::cpuwide_time_ms();
    int isolation_duration_ms = _isolation_duration_ms.load(butil::memory_order_relaxed);
    const int max_isolation_duration_ms =
        FLAGS_circuit_breaker_max_isolation_duration_ms;
    const int min_isolation_duration_ms =
        FLAGS_circuit_breaker_min_isolation_duration_ms;
    if (now_time_ms - _last_reset_time_ms < max_isolation_duration_ms) {
        isolation_duration_ms =
            std::min(isolation_duration_ms * 2, max_isolation_duration_ms);
    } else {
        isolation_duration_ms = min_isolation_duration_ms;
    }
    _isolation_duration_ms.store(isolation_duration_ms, butil::memory_order_relaxed);
}

}  // namespace brpc
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "vp9/decoder/vp9_thread.h"

#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/decode_test_driver.h"
#include "test/md5_helper.h"
#include "test/webm_video_source.h"

namespace {

class VP9WorkerThreadTest : public ::testing::TestWithParam<bool> {
 protected:
  virtual ~VP9WorkerThreadTest() {}
  virtual void SetUp() {
    vp9_worker_init(&worker_);
  }

  virtual void TearDown() {
    vp9_worker_end(&worker_);
  }

  VP9Worker worker_;
};

int ThreadHook(void* data, void* return_value) {
  int* const hook_data = reinterpret_cast<int*>(data);
  *hook_data = 5;
  return *reinterpret_cast<int*>(return_value);
}

TEST_P(VP9WorkerThreadTest, HookSuccess) {
  EXPECT_TRUE(vp9_worker_sync(&worker_));  // should be a no-op.

  for (int i = 0; i < 2; ++i) {
    EXPECT_TRUE(vp9_worker_reset(&worker_));

    int hook_data = 0;
    int return_value = 1;  // return successfully from the hook
    worker_.hook = ThreadHook;
    worker_.data1 = &hook_data;
    worker_.data2 = &return_value;

    const bool synchronous = GetParam();
    if (synchronous) {
      vp9_worker_execute(&worker_);
    } else {
      vp9_worker_launch(&worker_);
    }
    EXPECT_TRUE(vp9_worker_sync(&worker_));
    EXPECT_FALSE(worker_.had_error);
    EXPECT_EQ(5, hook_data);

    EXPECT_TRUE(vp9_worker_sync(&worker_));  // should be a no-op.
  }
}

TEST_P(VP9WorkerThreadTest, HookFailure) {
  EXPECT_TRUE(vp9_worker_reset(&worker_));

  int hook_data = 0;
  int return_value = 0;  // return failure from the hook
  worker_.hook = ThreadHook;
  worker_.data1 = &hook_data;
  worker_.data2 = &return_value;

  const bool synchronous = GetParam();
  if (synchronous) {
    vp9_worker_execute(&worker_);
  } else {
    vp9_worker_launch(&worker_);
  }
  EXPECT_FALSE(vp9_worker_sync(&worker_));
  EXPECT_TRUE(worker_.had_error);

  // Ensure _reset() clears the error and _launch() can be called again.
  return_value = 1;
  EXPECT_TRUE(vp9_worker_reset(&worker_));
  EXPECT_FALSE(worker_.had_error);
  vp9_worker_launch(&worker_);
  EXPECT_TRUE(vp9_worker_sync(&worker_));
  EXPECT_FALSE(worker_.had_error);
}

TEST(VP9DecodeMTTest, MTDecode) {
  libvpx_test::WebMVideoSource video("vp90-2-03-size-226x226.webm");
  video.Init();

  vpx_codec_dec_cfg_t cfg = {0};
  cfg.threads = 2;
  libvpx_test::VP9Decoder decoder(cfg, 0);

  libvpx_test::MD5 md5;
  for (video.Begin(); video.cxdata(); video.Next()) {
    const vpx_codec_err_t res =
        decoder.DecodeFrame(video.cxdata(), video.frame_size());
    ASSERT_EQ(VPX_CODEC_OK, res) << decoder.DecodeError();

    libvpx_test::DxDataIterator dec_iter = decoder.GetDxData();
    const vpx_image_t *img = NULL;

    // Get decompressed data
    while ((img = dec_iter.Next())) {
      md5.Add(img);
    }
  }
  EXPECT_STREQ("b35a1b707b28e82be025d960aba039bc", md5.Get());
}

INSTANTIATE_TEST_CASE_P(Synchronous, VP9WorkerThreadTest, ::testing::Bool());

}  // namespace
<commit_msg>vp9_thread_test: quiet some msvc int->bool warnings<commit_after>/*
 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "vp9/decoder/vp9_thread.h"

#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/decode_test_driver.h"
#include "test/md5_helper.h"
#include "test/webm_video_source.h"

namespace {

class VP9WorkerThreadTest : public ::testing::TestWithParam<bool> {
 protected:
  virtual ~VP9WorkerThreadTest() {}
  virtual void SetUp() {
    vp9_worker_init(&worker_);
  }

  virtual void TearDown() {
    vp9_worker_end(&worker_);
  }

  VP9Worker worker_;
};

int ThreadHook(void* data, void* return_value) {
  int* const hook_data = reinterpret_cast<int*>(data);
  *hook_data = 5;
  return *reinterpret_cast<int*>(return_value);
}

TEST_P(VP9WorkerThreadTest, HookSuccess) {
  EXPECT_NE(vp9_worker_sync(&worker_), 0);  // should be a no-op.

  for (int i = 0; i < 2; ++i) {
    EXPECT_NE(vp9_worker_reset(&worker_), 0);

    int hook_data = 0;
    int return_value = 1;  // return successfully from the hook
    worker_.hook = ThreadHook;
    worker_.data1 = &hook_data;
    worker_.data2 = &return_value;

    const bool synchronous = GetParam();
    if (synchronous) {
      vp9_worker_execute(&worker_);
    } else {
      vp9_worker_launch(&worker_);
    }
    EXPECT_NE(vp9_worker_sync(&worker_), 0);
    EXPECT_FALSE(worker_.had_error);
    EXPECT_EQ(5, hook_data);

    EXPECT_NE(vp9_worker_sync(&worker_), 0);  // should be a no-op.
  }
}

TEST_P(VP9WorkerThreadTest, HookFailure) {
  EXPECT_NE(vp9_worker_reset(&worker_), 0);

  int hook_data = 0;
  int return_value = 0;  // return failure from the hook
  worker_.hook = ThreadHook;
  worker_.data1 = &hook_data;
  worker_.data2 = &return_value;

  const bool synchronous = GetParam();
  if (synchronous) {
    vp9_worker_execute(&worker_);
  } else {
    vp9_worker_launch(&worker_);
  }
  EXPECT_FALSE(vp9_worker_sync(&worker_));
  EXPECT_EQ(1, worker_.had_error);

  // Ensure _reset() clears the error and _launch() can be called again.
  return_value = 1;
  EXPECT_NE(vp9_worker_reset(&worker_), 0);
  EXPECT_FALSE(worker_.had_error);
  vp9_worker_launch(&worker_);
  EXPECT_NE(vp9_worker_sync(&worker_), 0);
  EXPECT_FALSE(worker_.had_error);
}

TEST(VP9DecodeMTTest, MTDecode) {
  libvpx_test::WebMVideoSource video("vp90-2-03-size-226x226.webm");
  video.Init();

  vpx_codec_dec_cfg_t cfg = {0};
  cfg.threads = 2;
  libvpx_test::VP9Decoder decoder(cfg, 0);

  libvpx_test::MD5 md5;
  for (video.Begin(); video.cxdata(); video.Next()) {
    const vpx_codec_err_t res =
        decoder.DecodeFrame(video.cxdata(), video.frame_size());
    ASSERT_EQ(VPX_CODEC_OK, res) << decoder.DecodeError();

    libvpx_test::DxDataIterator dec_iter = decoder.GetDxData();
    const vpx_image_t *img = NULL;

    // Get decompressed data
    while ((img = dec_iter.Next())) {
      md5.Add(img);
    }
  }
  EXPECT_STREQ("b35a1b707b28e82be025d960aba039bc", md5.Get());
}

INSTANTIATE_TEST_CASE_P(Synchronous, VP9WorkerThreadTest, ::testing::Bool());

}  // namespace
<|endoftext|>


<commit_before>#include "brandes.h"

#include <iostream>
#include <queue>
#include <stack>
#include <algorithm>
#include <thread>
#include <vector>

template<typename T>
void Brandes<T>::process(const size_t &vertex_id, std::vector<fType> *BC_local)
{
    size_t vec_size = BC_local->size();
    std::stack<size_t> S;
    std::vector<std::vector<size_t>> P(vec_size);
    std::vector<int> sigma(vec_size);
    std::vector<int> d(vec_size);
    std::vector<fType> delta(vec_size);

    for (size_t w = 0; w < graph_.get_vertex_num(); w++) {
        sigma[w] = 0;
        d[w] = -1;
        delta[w] = 0;
    }

    sigma[vertex_id] = 1;
    d[vertex_id] = 0;

    std::queue<size_t> Q;
    Q.push(vertex_id);

    while (!Q.empty()) {
        size_t v = Q.front();
        Q.pop();
        S.push(v);

        for (const auto &w : graph_.get_neighbor_aliases(v)) {
            if (d[w] < 0) {
                Q.push(w);
                d[w] = d[v] + 1;
            }

            if (d[w] == d[v] + 1) {
                sigma[w] += sigma[v];
                P[w].emplace_back(v);
            }
        }
    }

    while (!S.empty()) {
        size_t v = S.top(); S.pop();

        for (size_t p : P[v]) {
            double result = (fType(sigma[p]) / sigma[v]) * (1.0 + delta[v]);
            delta[p] += result;
        }

        if (v != vertex_id) {
            (*BC_local)[v] += delta[v];
        }
    }

}

template<typename T>
void Brandes<T>::run_worker(std::atomic<size_t> *idx) {
    std::vector<fType> BC_local(graph_.get_vertex_num());

    std::fill(begin(BC_local), end(BC_local), 0.0);

    while(true) {
        int my_index = (*idx)--;

        if (my_index < 0)
            break;

        process(my_index, &BC_local);
    }

    // Synchronized section
    {
        std::cerr << "Scalam.." << std::endl;
        std::lock_guard<std::mutex> guard(bc_mutex_);
        for (int i = 0; i < BC_local.size(); i++) {
            BC_[i] += BC_local[i];
        }
    }
}

template<typename T>
void Brandes<T>::run(const size_t thread_num) {
    std::vector<std::thread> threads;

    std::atomic<size_t> index;

    index.store(graph_.get_vertex_num() - 1);

    // run threads
    for (size_t i = 0; i < thread_num-1; i++)
        threads.emplace_back(std::thread( [this, &index] { run_worker(&index); }));

    // start working
    run_worker(&index);

    // wait for others to finish
    for (auto& thread : threads)
        thread.join();
}

template<typename T>
const std::vector<std::pair<T, typename Brandes<T>::fType>>
Brandes<T>::get_result_vector() {
    std::vector<std::pair<T, fType>> results;
    results.reserve(BC_.size());

    for (size_t i = 0; i < BC_.size(); i++) {
        if(graph_.get_neighbor_aliases(i).size() > 0)
            results.emplace_back(std::make_pair(graph_.get_id_from_alias(i), BC_[i]));
    }

    sort(begin(results), end(results));
    return results;
}
<commit_msg>Reformat code<commit_after>#include "brandes.h"

#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <thread>
#include <vector>

template <typename T>
void Brandes<T>::process(const size_t& vertex_id, std::vector<fType>* BC_local)
{
    size_t vec_size = BC_local->size();
    std::stack<size_t> S;
    std::vector<std::vector<size_t> > P(vec_size);
    std::vector<int> sigma(vec_size);
    std::vector<int> d(vec_size);
    std::vector<fType> delta(vec_size);

    for (size_t w = 0; w < graph_.get_vertex_num(); w++) {
        sigma[w] = 0;
        d[w] = -1;
        delta[w] = 0;
    }

    sigma[vertex_id] = 1;
    d[vertex_id] = 0;

    std::queue<size_t> Q;
    Q.push(vertex_id);

    while (!Q.empty()) {
        size_t v = Q.front();
        Q.pop();
        S.push(v);

        for (const auto& w : graph_.get_neighbor_aliases(v)) {
            if (d[w] < 0) {
                Q.push(w);
                d[w] = d[v] + 1;
            }

            if (d[w] == d[v] + 1) {
                sigma[w] += sigma[v];
                P[w].emplace_back(v);
            }
        }
    }

    while (!S.empty()) {
        size_t v = S.top();
        S.pop();

        for (size_t p : P[v]) {
            double result = (fType(sigma[p]) / sigma[v]) * (1.0 + delta[v]);
            delta[p] += result;
        }

        if (v != vertex_id) {
            (*BC_local)[v] += delta[v];
        }
    }
}

template <typename T>
void Brandes<T>::run_worker(std::atomic<size_t>* idx)
{
    std::vector<fType> BC_local(graph_.get_vertex_num());
    std::fill(begin(BC_local), end(BC_local), 0.0);

    while (true) {
        int my_index = (*idx)--;

        if (my_index < 0)
            break;

        process(my_index, &BC_local);
    }

    // Synchronized section
    {
        std::lock_guard<std::mutex> guard(bc_mutex_);
        for (size_t i = 0; i < BC_local.size(); i++) {
            BC_[i] += BC_local[i];
        }
    }
}

template <typename T>
void Brandes<T>::run(const size_t thread_num)
{
    std::vector<std::thread> threads;
    std::atomic<size_t> index;

    index.store(graph_.get_vertex_num() - 1);

    // run threads
    for (size_t i = 0; i < thread_num - 1; i++)
        threads.emplace_back(std::thread([this, &index] { run_worker(&index); }));

    // start working
    run_worker(&index);

    // wait for others to finish
    for (auto& thread : threads)
        thread.join();
}

template <typename T>
const std::vector<std::pair<T, typename Brandes<T>::fType> > Brandes<T>::get_result_vector()
{
    std::vector<std::pair<T, fType> > results;
    results.reserve(BC_.size());

    for (size_t i = 0; i < BC_.size(); i++) {
        if (graph_.get_neighbor_aliases(i).size() > 0)
            results.emplace_back(std::make_pair(graph_.get_id_from_alias(i), BC_[i]));
    }

    sort(begin(results), end(results));
    return results;
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <iostream>
#include <vector>
// glm additional header to generate transformation matrices directly.
#include <glm/gtc/matrix_transform.hpp>
#include <cstring> // For memcopy depending on the platform.

#include "helpers/ProgramUtilities.h"
#include "Renderer.h"

Renderer::Renderer(){}

Renderer::~Renderer(){}

void Renderer::init(int width, int height){

	// Initialize the timer and pingpong.
	_timer = glfwGetTime();
	_pingpong = 0;
	// Setup projection matrix.
	_camera.screen(width, height);

	// Setup the framebuffer.
	_framebuffer = Framebuffer(width, height);
	_framebuffer.setup();

	// Query the renderer identifier, and the supported OpenGL version.
	const GLubyte* renderer = glGetString(GL_RENDERER);
	const GLubyte* version = glGetString(GL_VERSION);
	std::cout << "Renderer: " << renderer << std::endl;
	std::cout << "OpenGL version supported: " << version << std::endl;
	checkGLError();

	// GL options
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glFrontFace(GL_CCW);
	glCullFace(GL_BACK);
	checkGLError();

	// Setup light
	_light.position = glm::vec4(0.0f); // position will be updated at each frame
	_light.shininess = 25.0f;
	_light.Ia = glm::vec4(0.3f, 0.3f, 0.3f, 0.0f);
	_light.Id = glm::vec4(0.8f, 0.8f,0.8f, 0.0f);
	_light.Is = glm::vec4(1.0f, 1.0f, 1.0f, 0.0f);

	// Generate the buffer.
	glGenBuffers(1, &_ubo);
	// Bind the buffer.
	glBindBuffer(GL_UNIFORM_BUFFER, _ubo);

	// We need to know the alignment size if we want to store two uniform blocks in the same uniform buffer.
	GLint uboAlignSize = 0;
	glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uboAlignSize);
	// Compute the padding for the second block, it needs to be a multiple of uboAlignSize (typically uboAlignSize will be 256.)
	GLuint lightSize = 4*sizeof(glm::vec4) + 1*sizeof(float);
	_padding = ((lightSize/uboAlignSize)+1)*uboAlignSize;

	// Allocate enough memory to hold two copies of the Light struct.
	glBufferData(GL_UNIFORM_BUFFER, _padding + lightSize, NULL, GL_DYNAMIC_DRAW);

	// Bind the range allocated to the first version of the light.
	glBindBufferRange(GL_UNIFORM_BUFFER, 0, _ubo, 0, lightSize);
	// Submit the data.
	glBufferSubData(GL_UNIFORM_BUFFER, 0, lightSize, &_light);

	// Bind the range allocated to the second version of the light.
	glBindBufferRange(GL_UNIFORM_BUFFER, 1, _ubo, _padding, lightSize);
	// Submit the data.
	glBufferSubData(GL_UNIFORM_BUFFER, _padding, lightSize, &_light);

	glBindBuffer(GL_UNIFORM_BUFFER,0);

	// Initialize objects.
	_suzanne.init();
	_dragon.init();
	_plane.init();
	_skybox.init();
	_screen.init(_framebuffer.textureId());
	checkGLError();

}

void Renderer::draw(){

	// Compute the time elapsed since last frame
	float elapsed = glfwGetTime() - _timer;
	_timer = glfwGetTime();

	// Physics simulation
	physics(elapsed);

	// Update the light position (in view space).
	// Bind the buffer.
	glBindBuffer(GL_UNIFORM_BUFFER, _ubo);
	// Obtain a handle to the underlying memory.
	// We force the GPU to consider the memory region as unsynchronized:
	// even if it is used, it will be overwritten. As we alternate between
	// two sub-buffers indices ("ping-ponging"), we won't risk writing over
	// a currently used value.
	GLvoid * ptr = glMapBufferRange(GL_UNIFORM_BUFFER,_pingpong*_padding,sizeof(glm::vec4),GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
	// Copy the light position.
	std::memcpy(ptr, &(_light.position[0]), sizeof(glm::vec4));
	// Unmap, unbind.
	glUnmapBuffer(GL_UNIFORM_BUFFER);
	glBindBuffer(GL_UNIFORM_BUFFER, 0);

	// Draw the scene inside the framebuffer.
	_framebuffer.bind();

	glViewport(0, 0, _framebuffer._width, _framebuffer._height);
	// Set the clear color to white.
	glClearColor(1.0f,1.0f,1.0f,0.0f);
	// Clear the color and depth buffers.
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// Draw objects.
	_suzanne.draw(elapsed, _camera._view, _camera._projection, _pingpong);
	_dragon.draw(elapsed, _camera._view, _camera._projection, _pingpong);
	_plane.draw(elapsed, _camera._view, _camera._projection, _pingpong);
	_skybox.draw(elapsed, _camera._view, _camera._projection);

	// Unbind the framebuffer, we now use the default framebuffer.
	_framebuffer.unbind();
	// Only the final target should be in the sRGB space.
	glEnable(GL_FRAMEBUFFER_SRGB);

	// Draw the fullscreen quad
	glViewport(0,0,_camera._screenSize[0],_camera._screenSize[1]);
	// Set the clear color to black.
	glClearColor(0.0f,0.0f,0.0f,0.0f);
	// Clear the color and depth buffers.
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// Draw the screen quad.
	_screen.draw(_timer);

	glDisable(GL_FRAMEBUFFER_SRGB);

	// Update timer
	_timer = glfwGetTime();
	// Update pingpong
	_pingpong = (_pingpong + 1)%2;
}

void Renderer::physics(float elapsedTime){
	_camera.update(elapsedTime);
	// Compute the light position in view space.
	_light.position = _camera._view * glm::vec4(2.0f,2.0f,2.0f,1.0f);
}

void Renderer::clean(){
	// Clean objects.
	_suzanne.clean();
	_dragon.clean();
	_plane.clean();
	_skybox.clean();
	_screen.clean();
	_framebuffer.clean();
}

void Renderer::resize(int width, int height){
	//Update the size of the viewport.
	glViewport(0, 0, width, height);
	// Update the projection matrix.
	_camera.screen(width, height);
	// Resize the framebuffer.
	_framebuffer.resize(width, height);
}

void Renderer::keyPressed(int key, int action){
	if(action == GLFW_PRESS){
		_camera.key(key, true);
	} else if(action == GLFW_RELEASE) {
		_camera.key(key, false);
	}
}

void Renderer::buttonPressed(int button, int action, double x, double y){
	if (button == GLFW_MOUSE_BUTTON_LEFT) {
		if (action == GLFW_PRESS) {
			_camera.mouse(MouseMode::Start,x, y);
		} else if (action == GLFW_RELEASE) {
			_camera.mouse(MouseMode::End, 0.0, 0.0);
		}
	} else {
		std::cout << "Button: " << button << ", action: " << action << std::endl;
	}
}

void Renderer::mousePosition(int x, int y, bool leftPress, bool rightPress){
	if (leftPress){
		_camera.mouse(MouseMode::Move, float(x), float(y));
    }
}

<commit_msg>Fix the size of the framebuffer to 512x512.<commit_after>#include <stdio.h>
#include <iostream>
#include <vector>
// glm additional header to generate transformation matrices directly.
#include <glm/gtc/matrix_transform.hpp>
#include <cstring> // For memcopy depending on the platform.

#include "helpers/ProgramUtilities.h"
#include "Renderer.h"

Renderer::Renderer(){}

Renderer::~Renderer(){}

void Renderer::init(int width, int height){

	// Initialize the timer and pingpong.
	_timer = glfwGetTime();
	_pingpong = 0;
	// Setup projection matrix.
	_camera.screen(width, height);

	// Setup the framebuffer.
	_framebuffer = Framebuffer(512, 512);
	_framebuffer.setup();

	// Query the renderer identifier, and the supported OpenGL version.
	const GLubyte* renderer = glGetString(GL_RENDERER);
	const GLubyte* version = glGetString(GL_VERSION);
	std::cout << "Renderer: " << renderer << std::endl;
	std::cout << "OpenGL version supported: " << version << std::endl;
	checkGLError();

	// GL options
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glFrontFace(GL_CCW);
	glCullFace(GL_BACK);
	checkGLError();

	// Setup light
	_light.position = glm::vec4(0.0f); // position will be updated at each frame
	_light.shininess = 25.0f;
	_light.Ia = glm::vec4(0.3f, 0.3f, 0.3f, 0.0f);
	_light.Id = glm::vec4(0.8f, 0.8f,0.8f, 0.0f);
	_light.Is = glm::vec4(1.0f, 1.0f, 1.0f, 0.0f);

	// Generate the buffer.
	glGenBuffers(1, &_ubo);
	// Bind the buffer.
	glBindBuffer(GL_UNIFORM_BUFFER, _ubo);

	// We need to know the alignment size if we want to store two uniform blocks in the same uniform buffer.
	GLint uboAlignSize = 0;
	glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uboAlignSize);
	// Compute the padding for the second block, it needs to be a multiple of uboAlignSize (typically uboAlignSize will be 256.)
	GLuint lightSize = 4*sizeof(glm::vec4) + 1*sizeof(float);
	_padding = ((lightSize/uboAlignSize)+1)*uboAlignSize;

	// Allocate enough memory to hold two copies of the Light struct.
	glBufferData(GL_UNIFORM_BUFFER, _padding + lightSize, NULL, GL_DYNAMIC_DRAW);

	// Bind the range allocated to the first version of the light.
	glBindBufferRange(GL_UNIFORM_BUFFER, 0, _ubo, 0, lightSize);
	// Submit the data.
	glBufferSubData(GL_UNIFORM_BUFFER, 0, lightSize, &_light);

	// Bind the range allocated to the second version of the light.
	glBindBufferRange(GL_UNIFORM_BUFFER, 1, _ubo, _padding, lightSize);
	// Submit the data.
	glBufferSubData(GL_UNIFORM_BUFFER, _padding, lightSize, &_light);

	glBindBuffer(GL_UNIFORM_BUFFER,0);

	// Initialize objects.
	_suzanne.init();
	_dragon.init();
	_plane.init();
	_skybox.init();
	_screen.init(_framebuffer.textureId());
	checkGLError();

}

void Renderer::draw(){

	// Compute the time elapsed since last frame
	float elapsed = glfwGetTime() - _timer;
	_timer = glfwGetTime();

	// Physics simulation
	physics(elapsed);

	// Update the light position (in view space).
	// Bind the buffer.
	glBindBuffer(GL_UNIFORM_BUFFER, _ubo);
	// Obtain a handle to the underlying memory.
	// We force the GPU to consider the memory region as unsynchronized:
	// even if it is used, it will be overwritten. As we alternate between
	// two sub-buffers indices ("ping-ponging"), we won't risk writing over
	// a currently used value.
	GLvoid * ptr = glMapBufferRange(GL_UNIFORM_BUFFER,_pingpong*_padding,sizeof(glm::vec4),GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
	// Copy the light position.
	std::memcpy(ptr, &(_light.position[0]), sizeof(glm::vec4));
	// Unmap, unbind.
	glUnmapBuffer(GL_UNIFORM_BUFFER);
	glBindBuffer(GL_UNIFORM_BUFFER, 0);

	// Draw the scene inside the framebuffer.
	_framebuffer.bind();

	glViewport(0, 0, _framebuffer._width, _framebuffer._height);
	// Set the clear color to white.
	glClearColor(1.0f,1.0f,1.0f,0.0f);
	// Clear the color and depth buffers.
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// Draw objects.
	_suzanne.draw(elapsed, _camera._view, _camera._projection, _pingpong);
	_dragon.draw(elapsed, _camera._view, _camera._projection, _pingpong);
	_plane.draw(elapsed, _camera._view, _camera._projection, _pingpong);
	//_skybox.draw(elapsed, _camera._view, _camera._projection);

	// Unbind the framebuffer, we now use the default framebuffer.
	_framebuffer.unbind();
	// Only the final target should be in the sRGB space.
	glEnable(GL_FRAMEBUFFER_SRGB);

	// Draw the fullscreen quad
	glViewport(0,0,_camera._screenSize[0],_camera._screenSize[1]);
	// Set the clear color to black.
	glClearColor(0.0f,0.0f,0.0f,0.0f);
	// Clear the color and depth buffers.
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// Draw the screen quad.
	_screen.draw(_timer);

	glDisable(GL_FRAMEBUFFER_SRGB);

	// Update timer
	_timer = glfwGetTime();
	// Update pingpong
	_pingpong = (_pingpong + 1)%2;
}

void Renderer::physics(float elapsedTime){
	_camera.update(elapsedTime);
	// Compute the light position in view space.
	_light.position = _camera._view * glm::vec4(2.0f,2.0f,2.0f,1.0f);
}

void Renderer::clean(){
	// Clean objects.
	_suzanne.clean();
	_dragon.clean();
	_plane.clean();
	_skybox.clean();
	_screen.clean();
	_framebuffer.clean();
}

void Renderer::resize(int width, int height){
	//Update the size of the viewport.
	glViewport(0, 0, width, height);
	// Update the projection matrix.
	_camera.screen(width, height);
	// Resize the framebuffer.
	//_framebuffer.resize(width, height);
}

void Renderer::keyPressed(int key, int action){
	if(action == GLFW_PRESS){
		_camera.key(key, true);
	} else if(action == GLFW_RELEASE) {
		_camera.key(key, false);
	}
}

void Renderer::buttonPressed(int button, int action, double x, double y){
	if (button == GLFW_MOUSE_BUTTON_LEFT) {
		if (action == GLFW_PRESS) {
			_camera.mouse(MouseMode::Start,x, y);
		} else if (action == GLFW_RELEASE) {
			_camera.mouse(MouseMode::End, 0.0, 0.0);
		}
	} else {
		std::cout << "Button: " << button << ", action: " << action << std::endl;
	}
}

void Renderer::mousePosition(int x, int y, bool leftPress, bool rightPress){
	if (leftPress){
		_camera.mouse(MouseMode::Move, float(x), float(y));
    }
}

<|endoftext|>


<commit_before>
/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkMovie.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkUtils.h"

#include "gif_lib.h"

#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
#define DGifCloseFile(a, b) DGifCloseFile(a)
#endif

class SkGIFMovie : public SkMovie {
public:
    SkGIFMovie(SkStream* stream);
    virtual ~SkGIFMovie();

protected:
    virtual bool onGetInfo(Info*);
    virtual bool onSetTime(SkMSec);
    virtual bool onGetBitmap(SkBitmap*);

private:
    GifFileType* fGIF;
    int fCurrIndex;
    int fLastDrawIndex;
    SkBitmap fBackup;
    SkColor fPaintingColor;
};

static int Decode(GifFileType* fileType, GifByteType* out, int size) {
    SkStream* stream = (SkStream*) fileType->UserData;
    return (int) stream->read(out, size);
}

SkGIFMovie::SkGIFMovie(SkStream* stream)
{
#if GIFLIB_MAJOR < 5
    fGIF = DGifOpen( stream, Decode );
#else
    fGIF = DGifOpen( stream, Decode, nullptr );
#endif
    if (nullptr == fGIF)
        return;

    if (DGifSlurp(fGIF) != GIF_OK)
    {
        DGifCloseFile(fGIF, nullptr);
        fGIF = nullptr;
    }
    fCurrIndex = -1;
    fLastDrawIndex = -1;
    fPaintingColor = SkPackARGB32(0, 0, 0, 0);
}

SkGIFMovie::~SkGIFMovie()
{
    if (fGIF)
        DGifCloseFile(fGIF, nullptr);
}

static SkMSec savedimage_duration(const SavedImage* image)
{
    for (int j = 0; j < image->ExtensionBlockCount; j++)
    {
        if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
        {
            SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
            const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
            return ((b[2] << 8) | b[1]) * 10;
        }
    }
    return 0;
}

bool SkGIFMovie::onGetInfo(Info* info)
{
    if (nullptr == fGIF)
        return false;

    SkMSec dur = 0;
    for (int i = 0; i < fGIF->ImageCount; i++)
        dur += savedimage_duration(&fGIF->SavedImages[i]);

    info->fDuration = dur;
    info->fWidth = fGIF->SWidth;
    info->fHeight = fGIF->SHeight;
    info->fIsOpaque = false;    // how to compute?
    return true;
}

bool SkGIFMovie::onSetTime(SkMSec time)
{
    if (nullptr == fGIF)
        return false;

    SkMSec dur = 0;
    for (int i = 0; i < fGIF->ImageCount; i++)
    {
        dur += savedimage_duration(&fGIF->SavedImages[i]);
        if (dur >= time)
        {
            fCurrIndex = i;
            return fLastDrawIndex != fCurrIndex;
        }
    }
    fCurrIndex = fGIF->ImageCount - 1;
    return true;
}

static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
                     int transparent, int width)
{
    for (; width > 0; width--, src++, dst++) {
        if (*src != transparent) {
            const GifColorType& col = cmap->Colors[*src];
            *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
        }
    }
}

#if GIFLIB_MAJOR < 5
static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
                               const ColorMapObject* cmap, int transparent, int copyWidth,
                               int copyHeight, const GifImageDesc& imageDesc, int rowStep,
                               int startRow)
{
    int row;
    // every 'rowStep'th row, starting with row 'startRow'
    for (row = startRow; row < copyHeight; row += rowStep) {
        uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
        copyLine(dst, src, cmap, transparent, copyWidth);
        src += imageDesc.Width;
    }

    // pad for rest height
    src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
}

static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
                          int transparent)
{
    int width = bm->width();
    int height = bm->height();
    GifWord copyWidth = frame->ImageDesc.Width;
    if (frame->ImageDesc.Left + copyWidth > width) {
        copyWidth = width - frame->ImageDesc.Left;
    }

    GifWord copyHeight = frame->ImageDesc.Height;
    if (frame->ImageDesc.Top + copyHeight > height) {
        copyHeight = height - frame->ImageDesc.Top;
    }

    // deinterlace
    const unsigned char* src = (unsigned char*)frame->RasterBits;

    // group 1 - every 8th row, starting with row 0
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);

    // group 2 - every 8th row, starting with row 4
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);

    // group 3 - every 4th row, starting with row 2
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);

    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
}
#endif

static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
                       int transparent)
{
    int width = bm->width();
    int height = bm->height();
    const unsigned char* src = (unsigned char*)frame->RasterBits;
    uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
    GifWord copyWidth = frame->ImageDesc.Width;
    if (frame->ImageDesc.Left + copyWidth > width) {
        copyWidth = width - frame->ImageDesc.Left;
    }

    GifWord copyHeight = frame->ImageDesc.Height;
    if (frame->ImageDesc.Top + copyHeight > height) {
        copyHeight = height - frame->ImageDesc.Top;
    }

    for (; copyHeight > 0; copyHeight--) {
        copyLine(dst, src, cmap, transparent, copyWidth);
        src += frame->ImageDesc.Width;
        dst += width;
    }
}

static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
                     uint32_t col)
{
    int bmWidth = bm->width();
    int bmHeight = bm->height();
    uint32_t* dst = bm->getAddr32(left, top);
    GifWord copyWidth = width;
    if (left + copyWidth > bmWidth) {
        copyWidth = bmWidth - left;
    }

    GifWord copyHeight = height;
    if (top + copyHeight > bmHeight) {
        copyHeight = bmHeight - top;
    }

    for (; copyHeight > 0; copyHeight--) {
        sk_memset32(dst, col, copyWidth);
        dst += bmWidth;
    }
}

static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
{
    int transparent = -1;

    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            bool has_transparency = ((eb->Bytes[0] & 1) == 1);
            if (has_transparency) {
                transparent = (unsigned char)eb->Bytes[3];
            }
        }
    }

    if (frame->ImageDesc.ColorMap != nullptr) {
        // use local color table
        cmap = frame->ImageDesc.ColorMap;
    }

    if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
        SkDEBUGFAIL("bad colortable setup");
        return;
    }

#if GIFLIB_MAJOR < 5
    // before GIFLIB 5, de-interlacing wasn't done by library at load time
    if (frame->ImageDesc.Interlace) {
        blitInterlace(bm, frame, cmap, transparent);
        return;
    }
#endif

    blitNormal(bm, frame, cmap, transparent);
}

static bool checkIfWillBeCleared(const SavedImage* frame)
{
    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            // check disposal method
            int disposal = ((eb->Bytes[0] >> 2) & 7);
            if (disposal == 2 || disposal == 3) {
                return true;
            }
        }
    }
    return false;
}

static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
{
    *trans = false;
    *disposal = 0;
    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            *trans = ((eb->Bytes[0] & 1) == 1);
            *disposal = ((eb->Bytes[0] >> 2) & 7);
        }
    }
}

// return true if area of 'target' is completely covers area of 'covered'
static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
{
    if (target->ImageDesc.Left <= covered->ImageDesc.Left
        && covered->ImageDesc.Left + covered->ImageDesc.Width <=
               target->ImageDesc.Left + target->ImageDesc.Width
        && target->ImageDesc.Top <= covered->ImageDesc.Top
        && covered->ImageDesc.Top + covered->ImageDesc.Height <=
               target->ImageDesc.Top + target->ImageDesc.Height) {
        return true;
    }
    return false;
}

static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
                                 SkBitmap* backup, SkColor color)
{
    // We can skip disposal process if next frame is not transparent
    // and completely covers current area
    bool curTrans;
    int curDisposal;
    getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
    bool nextTrans;
    int nextDisposal;
    getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
    if ((curDisposal == 2 || curDisposal == 3)
        && (nextTrans || !checkIfCover(next, cur))) {
        switch (curDisposal) {
        // restore to background color
        // -> 'background' means background under this image.
        case 2:
            fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
                     cur->ImageDesc.Width, cur->ImageDesc.Height,
                     color);
            break;

        // restore to previous
        case 3:
            bm->swap(*backup);
            break;
        }
    }

    // Save current image if next frame's disposal method == 3
    if (nextDisposal == 3) {
        const uint32_t* src = bm->getAddr32(0, 0);
        uint32_t* dst = backup->getAddr32(0, 0);
        int cnt = bm->width() * bm->height();
        memcpy(dst, src, cnt*sizeof(uint32_t));
    }
}

bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
{
    const GifFileType* gif = fGIF;
    if (nullptr == gif)
        return false;

    if (gif->ImageCount < 1) {
        return false;
    }

    const int width = gif->SWidth;
    const int height = gif->SHeight;
    if (width <= 0 || height <= 0) {
        return false;
    }

    // no need to draw
    if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
        return true;
    }

    int startIndex = fLastDrawIndex + 1;
    if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
        // first time

        startIndex = 0;

        // create bitmap
        if (!bm->tryAllocN32Pixels(width, height)) {
            return false;
        }
        // create bitmap for backup
        if (!fBackup.tryAllocN32Pixels(width, height)) {
            return false;
        }
    } else if (startIndex > fCurrIndex) {
        // rewind to 1st frame for repeat
        startIndex = 0;
    }

    int lastIndex = fCurrIndex;
    if (lastIndex < 0) {
        // first time
        lastIndex = 0;
    } else if (lastIndex > fGIF->ImageCount - 1) {
        // this block must not be reached.
        lastIndex = fGIF->ImageCount - 1;
    }

    SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
    if (gif->SColorMap != nullptr) {
        const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
        bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
    }

    // draw each frames - not intelligent way
    for (int i = startIndex; i <= lastIndex; i++) {
        const SavedImage* cur = &fGIF->SavedImages[i];
        if (i == 0) {
            bool trans;
            int disposal;
            getTransparencyAndDisposalMethod(cur, &trans, &disposal);
            if (!trans && gif->SColorMap != nullptr) {
                fPaintingColor = bgColor;
            } else {
                fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
            }

            bm->eraseColor(fPaintingColor);
            fBackup.eraseColor(fPaintingColor);
        } else {
            // Dispose previous frame before move to next frame.
            const SavedImage* prev = &fGIF->SavedImages[i-1];
            disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor);
        }

        // Draw frame
        // We can skip this process if this index is not last and disposal
        // method == 2 or method == 3
        if (i == lastIndex || !checkIfWillBeCleared(cur)) {
            drawFrame(bm, cur, gif->SColorMap);
        }
    }

    // save index
    fLastDrawIndex = lastIndex;
    return true;
}

///////////////////////////////////////////////////////////////////////////////

#include "SkTRegistry.h"

SkMovie* Factory(SkStreamRewindable* stream) {
    char buf[GIF_STAMP_LEN];
    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
            // must rewind here, since our construct wants to re-read the data
            stream->rewind();
            return new SkGIFMovie(stream);
        }
    }
    return nullptr;
}

static SkTRegistry<SkMovie*(*)(SkStreamRewindable*)> gReg(Factory);
<commit_msg>Fix out of bounds memory read in GIFMovie.cpp am: 16882f7212 am: d2456e3a12 am: ffc0958605 am: df18259ff1 am: c9b947def7 am: 09ad23b21e am: 716de6b276 am: d1fb426850 am: d80a31cff8<commit_after>
/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkMovie.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkUtils.h"

#include "gif_lib.h"

#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
#define DGifCloseFile(a, b) DGifCloseFile(a)
#endif

class SkGIFMovie : public SkMovie {
public:
    SkGIFMovie(SkStream* stream);
    virtual ~SkGIFMovie();

protected:
    virtual bool onGetInfo(Info*);
    virtual bool onSetTime(SkMSec);
    virtual bool onGetBitmap(SkBitmap*);

private:
    GifFileType* fGIF;
    int fCurrIndex;
    int fLastDrawIndex;
    SkBitmap fBackup;
    SkColor fPaintingColor;
};

static int Decode(GifFileType* fileType, GifByteType* out, int size) {
    SkStream* stream = (SkStream*) fileType->UserData;
    return (int) stream->read(out, size);
}

SkGIFMovie::SkGIFMovie(SkStream* stream)
{
#if GIFLIB_MAJOR < 5
    fGIF = DGifOpen( stream, Decode );
#else
    fGIF = DGifOpen( stream, Decode, nullptr );
#endif
    if (nullptr == fGIF)
        return;

    if (DGifSlurp(fGIF) != GIF_OK)
    {
        DGifCloseFile(fGIF, nullptr);
        fGIF = nullptr;
    }
    fCurrIndex = -1;
    fLastDrawIndex = -1;
    fPaintingColor = SkPackARGB32(0, 0, 0, 0);
}

SkGIFMovie::~SkGIFMovie()
{
    if (fGIF)
        DGifCloseFile(fGIF, nullptr);
}

static SkMSec savedimage_duration(const SavedImage* image)
{
    for (int j = 0; j < image->ExtensionBlockCount; j++)
    {
        if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
        {
            SkASSERT(image->ExtensionBlocks[j].ByteCount >= 4);
            const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
            return ((b[2] << 8) | b[1]) * 10;
        }
    }
    return 0;
}

bool SkGIFMovie::onGetInfo(Info* info)
{
    if (nullptr == fGIF)
        return false;

    SkMSec dur = 0;
    for (int i = 0; i < fGIF->ImageCount; i++)
        dur += savedimage_duration(&fGIF->SavedImages[i]);

    info->fDuration = dur;
    info->fWidth = fGIF->SWidth;
    info->fHeight = fGIF->SHeight;
    info->fIsOpaque = false;    // how to compute?
    return true;
}

bool SkGIFMovie::onSetTime(SkMSec time)
{
    if (nullptr == fGIF)
        return false;

    SkMSec dur = 0;
    for (int i = 0; i < fGIF->ImageCount; i++)
    {
        dur += savedimage_duration(&fGIF->SavedImages[i]);
        if (dur >= time)
        {
            fCurrIndex = i;
            return fLastDrawIndex != fCurrIndex;
        }
    }
    fCurrIndex = fGIF->ImageCount - 1;
    return true;
}

static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
                     int transparent, int width)
{
    for (; width > 0; width--, src++, dst++) {
        if (*src != transparent && *src < cmap->ColorCount) {
            const GifColorType& col = cmap->Colors[*src];
            *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
        }
    }
}

#if GIFLIB_MAJOR < 5
static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
                               const ColorMapObject* cmap, int transparent, int copyWidth,
                               int copyHeight, const GifImageDesc& imageDesc, int rowStep,
                               int startRow)
{
    int row;
    // every 'rowStep'th row, starting with row 'startRow'
    for (row = startRow; row < copyHeight; row += rowStep) {
        uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
        copyLine(dst, src, cmap, transparent, copyWidth);
        src += imageDesc.Width;
    }

    // pad for rest height
    src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
}

static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
                          int transparent)
{
    int width = bm->width();
    int height = bm->height();
    GifWord copyWidth = frame->ImageDesc.Width;
    if (frame->ImageDesc.Left + copyWidth > width) {
        copyWidth = width - frame->ImageDesc.Left;
    }

    GifWord copyHeight = frame->ImageDesc.Height;
    if (frame->ImageDesc.Top + copyHeight > height) {
        copyHeight = height - frame->ImageDesc.Top;
    }

    // deinterlace
    const unsigned char* src = (unsigned char*)frame->RasterBits;

    // group 1 - every 8th row, starting with row 0
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);

    // group 2 - every 8th row, starting with row 4
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);

    // group 3 - every 4th row, starting with row 2
    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);

    copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
}
#endif

static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
                       int transparent)
{
    int width = bm->width();
    int height = bm->height();
    const unsigned char* src = (unsigned char*)frame->RasterBits;
    uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
    GifWord copyWidth = frame->ImageDesc.Width;
    if (frame->ImageDesc.Left + copyWidth > width) {
        copyWidth = width - frame->ImageDesc.Left;
    }

    GifWord copyHeight = frame->ImageDesc.Height;
    if (frame->ImageDesc.Top + copyHeight > height) {
        copyHeight = height - frame->ImageDesc.Top;
    }

    for (; copyHeight > 0; copyHeight--) {
        copyLine(dst, src, cmap, transparent, copyWidth);
        src += frame->ImageDesc.Width;
        dst += width;
    }
}

static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
                     uint32_t col)
{
    int bmWidth = bm->width();
    int bmHeight = bm->height();
    uint32_t* dst = bm->getAddr32(left, top);
    GifWord copyWidth = width;
    if (left + copyWidth > bmWidth) {
        copyWidth = bmWidth - left;
    }

    GifWord copyHeight = height;
    if (top + copyHeight > bmHeight) {
        copyHeight = bmHeight - top;
    }

    for (; copyHeight > 0; copyHeight--) {
        sk_memset32(dst, col, copyWidth);
        dst += bmWidth;
    }
}

static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
{
    int transparent = -1;

    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            bool has_transparency = ((eb->Bytes[0] & 1) == 1);
            if (has_transparency) {
                transparent = (unsigned char)eb->Bytes[3];
            }
        }
    }

    if (frame->ImageDesc.ColorMap != nullptr) {
        // use local color table
        cmap = frame->ImageDesc.ColorMap;
    }

    if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
        SkDEBUGFAIL("bad colortable setup");
        return;
    }

#if GIFLIB_MAJOR < 5
    // before GIFLIB 5, de-interlacing wasn't done by library at load time
    if (frame->ImageDesc.Interlace) {
        blitInterlace(bm, frame, cmap, transparent);
        return;
    }
#endif

    blitNormal(bm, frame, cmap, transparent);
}

static bool checkIfWillBeCleared(const SavedImage* frame)
{
    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            // check disposal method
            int disposal = ((eb->Bytes[0] >> 2) & 7);
            if (disposal == 2 || disposal == 3) {
                return true;
            }
        }
    }
    return false;
}

static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
{
    *trans = false;
    *disposal = 0;
    for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
        ExtensionBlock* eb = frame->ExtensionBlocks + i;
        if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
            eb->ByteCount == 4) {
            *trans = ((eb->Bytes[0] & 1) == 1);
            *disposal = ((eb->Bytes[0] >> 2) & 7);
        }
    }
}

// return true if area of 'target' is completely covers area of 'covered'
static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
{
    if (target->ImageDesc.Left <= covered->ImageDesc.Left
        && covered->ImageDesc.Left + covered->ImageDesc.Width <=
               target->ImageDesc.Left + target->ImageDesc.Width
        && target->ImageDesc.Top <= covered->ImageDesc.Top
        && covered->ImageDesc.Top + covered->ImageDesc.Height <=
               target->ImageDesc.Top + target->ImageDesc.Height) {
        return true;
    }
    return false;
}

static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
                                 SkBitmap* backup, SkColor color)
{
    // We can skip disposal process if next frame is not transparent
    // and completely covers current area
    bool curTrans;
    int curDisposal;
    getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
    bool nextTrans;
    int nextDisposal;
    getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
    if ((curDisposal == 2 || curDisposal == 3)
        && (nextTrans || !checkIfCover(next, cur))) {
        switch (curDisposal) {
        // restore to background color
        // -> 'background' means background under this image.
        case 2:
            fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
                     cur->ImageDesc.Width, cur->ImageDesc.Height,
                     color);
            break;

        // restore to previous
        case 3:
            bm->swap(*backup);
            break;
        }
    }

    // Save current image if next frame's disposal method == 3
    if (nextDisposal == 3) {
        const uint32_t* src = bm->getAddr32(0, 0);
        uint32_t* dst = backup->getAddr32(0, 0);
        int cnt = bm->width() * bm->height();
        memcpy(dst, src, cnt*sizeof(uint32_t));
    }
}

bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
{
    const GifFileType* gif = fGIF;
    if (nullptr == gif)
        return false;

    if (gif->ImageCount < 1) {
        return false;
    }

    const int width = gif->SWidth;
    const int height = gif->SHeight;
    if (width <= 0 || height <= 0) {
        return false;
    }

    // no need to draw
    if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
        return true;
    }

    int startIndex = fLastDrawIndex + 1;
    if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
        // first time

        startIndex = 0;

        // create bitmap
        if (!bm->tryAllocN32Pixels(width, height)) {
            return false;
        }
        // create bitmap for backup
        if (!fBackup.tryAllocN32Pixels(width, height)) {
            return false;
        }
    } else if (startIndex > fCurrIndex) {
        // rewind to 1st frame for repeat
        startIndex = 0;
    }

    int lastIndex = fCurrIndex;
    if (lastIndex < 0) {
        // first time
        lastIndex = 0;
    } else if (lastIndex > fGIF->ImageCount - 1) {
        // this block must not be reached.
        lastIndex = fGIF->ImageCount - 1;
    }

    SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
    if (gif->SColorMap != nullptr) {
        const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
        bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
    }

    // draw each frames - not intelligent way
    for (int i = startIndex; i <= lastIndex; i++) {
        const SavedImage* cur = &fGIF->SavedImages[i];
        if (i == 0) {
            bool trans;
            int disposal;
            getTransparencyAndDisposalMethod(cur, &trans, &disposal);
            if (!trans && gif->SColorMap != nullptr) {
                fPaintingColor = bgColor;
            } else {
                fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
            }

            bm->eraseColor(fPaintingColor);
            fBackup.eraseColor(fPaintingColor);
        } else {
            // Dispose previous frame before move to next frame.
            const SavedImage* prev = &fGIF->SavedImages[i-1];
            disposeFrameIfNeeded(bm, prev, cur, &fBackup, fPaintingColor);
        }

        // Draw frame
        // We can skip this process if this index is not last and disposal
        // method == 2 or method == 3
        if (i == lastIndex || !checkIfWillBeCleared(cur)) {
            drawFrame(bm, cur, gif->SColorMap);
        }
    }

    // save index
    fLastDrawIndex = lastIndex;
    return true;
}

///////////////////////////////////////////////////////////////////////////////

#include "SkTRegistry.h"

SkMovie* Factory(SkStreamRewindable* stream) {
    char buf[GIF_STAMP_LEN];
    if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
        if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
                memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
            // must rewind here, since our construct wants to re-read the data
            stream->rewind();
            return new SkGIFMovie(stream);
        }
    }
    return nullptr;
}

static SkTRegistry<SkMovie*(*)(SkStreamRewindable*)> gReg(Factory);
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

//
// This file was designed to be directly included from itkFloatingPointExceptions
// cxx files.
//

#include "itkConfigure.h"
#include "itkConfigurePrivate.h"
#include "itkFloatingPointExceptions.h"

#ifdef LINUX
/* BEGIN quote
http://graphviz.sourcearchive.com/documentation/2.16/gvrender__pango_8c-source.html
*/
/* _GNU_SOURCE is needed (supposedly) for the feenableexcept
 * prototype to be defined in fenv.h on GNU systems.
 * Presumably it will do no harm on other systems.
 */
#  ifndef _GNU_SOURCE
#    define _GNU_SOURCE
#  endif

/* We are not supposed to need __USE_GNU, but I can't see
 * how to get the prototype for fedisableexcept from
 * /usr/include/fenv.h without it.
 */
#  ifndef __USE_GNU
#    define __USE_GNU
#  endif
/* END quote */
#endif // LINUX

#include <cfenv>

//-----------------------------------------------------------------------------
// feenableexcept/fedisableexcept implementations
//-----------------------------------------------------------------------------

// Define ITK_FEENABLEEXCEPT_NOOP if feenableexcept and fedisableexcept functions
// should be do nothing (beside of returning zero).
#if defined(__sun) || defined(__EMSCRIPTEN__)
#  define ITK_FEENABLEEXCEPT_NOOP
#endif

// We do not have ITK_HAS_FEENABLEEXCEPT, and we do not have a workaround
// implemented, e.g. ARMv8 with MUSL
#if !defined(ITK_HAS_FEENABLEEXCEPT) && !defined(__ppc__) && !defined(__ppc64__) && !defined(__i386__) &&              \
  !defined(__x86_64__)
#  define ITK_FEENABLEEXCEPT_NOOP
#endif

#if defined(__APPLE__)
#  include "TargetConditionals.h"
#  if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR || TARGET_CPU_ARM64
#    define ITK_FEENABLEEXCEPT_NOOP
#  endif
#endif

// Considering the following macros:
//
// * ITK_FEENABLEEXCEPT_NOOP   : If it applies, defined above
// * ITK_HAS_FEENABLEEXCEPT    : If it applies, defined in itkConfigure.h based on try_compile
// * ITK_HAS_FEGETENV          : idem
// * ITK_HAS_FESETENV          : idem
//
// the logic below will
//
// (1) include different implementations for the feenableexcept and fedisableexcept
//     functions
//
// (2) and will also define the macros:
//
// * ITK_HAS_FPE_CAPABILITY      : (a) If defined, enable catching of the different types
//                                      of floating point exceptions.
//                                 (b) If not defined, display "FloatingPointExceptions are not supported on this
//                                 platform.".
//                                     Then, depending on the value of FloatingPointExceptions::GetExceptionAction(),
//                                     it aborts or exits
//
// * ITK_FPE_USE_SIGNAL_HANDLER  : (a) If defined, install handler that reports
//                                     information about the exception that invoked it.
//                                     Then, depending on the value of FloatingPointExceptions::GetExceptionAction(),
//                                     it aborts or exits
//                                 (b) If not defined
//
// which ultimately define the implementation of FloatingPointExceptions::Enable()
// and FloatingPointExceptions::Disable().

#if defined(ITK_FEENABLEEXCEPT_NOOP)
static int
itk_feenableexcept(unsigned int /*excepts*/)
{
  return 0;
}
static int
itk_fedisableexcept(unsigned int /*excepts*/)
{
  return 0;
}
#  define ITK_HAS_FPE_CAPABILITY

#elif defined(ITK_HAS_FEENABLEEXCEPT)
#  define itk_feenableexcept feenableexcept
#  define itk_fedisableexcept fedisableexcept
#  define ITK_HAS_FPE_CAPABILITY
#  define ITK_FPE_USE_SIGNAL_HANDLER

#elif defined(ITK_HAS_FEGETENV) && defined(ITK_HAS_FESETENV)
#  include "itkFloatingPointExceptions_unix_feenableexcept_using_fegetenv.cxx"
#  define ITK_HAS_FPE_CAPABILITY
#  define ITK_FPE_USE_SIGNAL_HANDLER

#endif

// Implementation of signal handler used below
#if defined(ITK_FPE_USE_SIGNAL_HANDLER)
#  include "itkFloatingPointExceptions_unix_signalhandler.cxx"
#endif

#include <cstring> // memset

namespace itk
{

void
FloatingPointExceptions::Enable()
{
  itkInitGlobalsMacro(PimplGlobals);
#if defined(ITK_HAS_FPE_CAPABILITY)
  itk_feenableexcept(FE_DIVBYZERO);
  itk_feenableexcept(FE_INVALID);
#  if defined(ITK_FPE_USE_SIGNAL_HANDLER)
  struct sigaction act;
  memset(&act, 0, sizeof(struct sigaction));
  act.sa_sigaction = fhdl;
  sigemptyset(&act.sa_mask);
  act.sa_flags = SA_SIGINFO;
  sigaction(SIGFPE, &act, nullptr);
#  endif
  FloatingPointExceptions::m_PimplGlobals->m_Enabled = true;
  (void)itkFloatingPointExceptionsNotSupported; // avoid unused-function warning
#else
  itkFloatingPointExceptionsNotSupported();
#endif
}

void
FloatingPointExceptions::Disable()
{
  itkInitGlobalsMacro(PimplGlobals);
#if defined(ITK_HAS_FPE_CAPABILITY)
  itk_fedisableexcept(FE_DIVBYZERO);
  itk_fedisableexcept(FE_INVALID);
  FloatingPointExceptions::m_PimplGlobals->m_Enabled = false;
#else
  itkFloatingPointExceptionsNotSupported();
#endif
}

bool
FloatingPointExceptions::HasFloatingPointExceptionsSupport()
{
  itkInitGlobalsMacro(PimplGlobals);
#if defined(ITK_HAS_FPE_CAPABILITY)
  return true;
#else
  return false;
#endif
}

} // namespace itk
<commit_msg>COMP: Provide default values for floating point macros<commit_after>/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

//
// This file was designed to be directly included from itkFloatingPointExceptions
// cxx files.
//

#include "itkConfigure.h"
#include "itkConfigurePrivate.h"
#include "itkFloatingPointExceptions.h"

#ifdef LINUX
/* BEGIN quote
http://graphviz.sourcearchive.com/documentation/2.16/gvrender__pango_8c-source.html
*/
/* _GNU_SOURCE is needed (supposedly) for the feenableexcept
 * prototype to be defined in fenv.h on GNU systems.
 * Presumably it will do no harm on other systems.
 */
#  ifndef _GNU_SOURCE
#    define _GNU_SOURCE
#  endif

/* We are not supposed to need __USE_GNU, but I can't see
 * how to get the prototype for fedisableexcept from
 * /usr/include/fenv.h without it.
 */
#  ifndef __USE_GNU
#    define __USE_GNU
#  endif
/* END quote */
#endif // LINUX

#include <cfenv>

//-----------------------------------------------------------------------------
// feenableexcept/fedisableexcept implementations
//-----------------------------------------------------------------------------

// Define ITK_FEENABLEEXCEPT_NOOP if feenableexcept and fedisableexcept functions
// should be do nothing (beside of returning zero).
#if defined(__sun) || defined(__EMSCRIPTEN__)
#  define ITK_FEENABLEEXCEPT_NOOP
#endif

// We do not have ITK_HAS_FEENABLEEXCEPT, and we do not have a workaround
// implemented, e.g. ARMv8 with MUSL
#if !defined(ITK_HAS_FEENABLEEXCEPT) && !defined(__ppc__) && !defined(__ppc64__) && !defined(__i386__) &&              \
  !defined(__x86_64__)
#  define ITK_FEENABLEEXCEPT_NOOP
#endif

#if defined(__APPLE__)
#  include "TargetConditionals.h"
#  if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR || TARGET_CPU_ARM64
#    define ITK_FEENABLEEXCEPT_NOOP
#  endif
#endif

// Define used macros with defaults if not available
#if defined(ITK_FEENABLEEXCEPT_NOOP)
#  if !defined(FE_DIVBYZERO)
#    define FE_DIVBYZERO 4
#  endif
#  if !defined(FE_INVALID)
#    define FE_INVALID 1
#  endif
#endif

// Considering the following macros:
//
// * ITK_FEENABLEEXCEPT_NOOP   : If it applies, defined above
// * ITK_HAS_FEENABLEEXCEPT    : If it applies, defined in itkConfigure.h based on try_compile
// * ITK_HAS_FEGETENV          : idem
// * ITK_HAS_FESETENV          : idem
//
// the logic below will
//
// (1) include different implementations for the feenableexcept and fedisableexcept
//     functions
//
// (2) and will also define the macros:
//
// * ITK_HAS_FPE_CAPABILITY      : (a) If defined, enable catching of the different types
//                                      of floating point exceptions.
//                                 (b) If not defined, display "FloatingPointExceptions are not supported on this
//                                 platform.".
//                                     Then, depending on the value of FloatingPointExceptions::GetExceptionAction(),
//                                     it aborts or exits
//
// * ITK_FPE_USE_SIGNAL_HANDLER  : (a) If defined, install handler that reports
//                                     information about the exception that invoked it.
//                                     Then, depending on the value of FloatingPointExceptions::GetExceptionAction(),
//                                     it aborts or exits
//                                 (b) If not defined
//
// which ultimately define the implementation of FloatingPointExceptions::Enable()
// and FloatingPointExceptions::Disable().

#if defined(ITK_FEENABLEEXCEPT_NOOP)
static int
itk_feenableexcept(unsigned int /*excepts*/)
{
  return 0;
}
static int
itk_fedisableexcept(unsigned int /*excepts*/)
{
  return 0;
}
#  define ITK_HAS_FPE_CAPABILITY

#elif defined(ITK_HAS_FEENABLEEXCEPT)
#  define itk_feenableexcept feenableexcept
#  define itk_fedisableexcept fedisableexcept
#  define ITK_HAS_FPE_CAPABILITY
#  define ITK_FPE_USE_SIGNAL_HANDLER

#elif defined(ITK_HAS_FEGETENV) && defined(ITK_HAS_FESETENV)
#  include "itkFloatingPointExceptions_unix_feenableexcept_using_fegetenv.cxx"
#  define ITK_HAS_FPE_CAPABILITY
#  define ITK_FPE_USE_SIGNAL_HANDLER

#endif

// Implementation of signal handler used below
#if defined(ITK_FPE_USE_SIGNAL_HANDLER)
#  include "itkFloatingPointExceptions_unix_signalhandler.cxx"
#endif

#include <cstring> // memset

namespace itk
{

void
FloatingPointExceptions::Enable()
{
  itkInitGlobalsMacro(PimplGlobals);
#if defined(ITK_HAS_FPE_CAPABILITY)
  itk_feenableexcept(FE_DIVBYZERO);
  itk_feenableexcept(FE_INVALID);
#  if defined(ITK_FPE_USE_SIGNAL_HANDLER)
  struct sigaction act;
  memset(&act, 0, sizeof(struct sigaction));
  act.sa_sigaction = fhdl;
  sigemptyset(&act.sa_mask);
  act.sa_flags = SA_SIGINFO;
  sigaction(SIGFPE, &act, nullptr);
#  endif
  FloatingPointExceptions::m_PimplGlobals->m_Enabled = true;
  (void)itkFloatingPointExceptionsNotSupported; // avoid unused-function warning
#else
  itkFloatingPointExceptionsNotSupported();
#endif
}

void
FloatingPointExceptions::Disable()
{
  itkInitGlobalsMacro(PimplGlobals);
#if defined(ITK_HAS_FPE_CAPABILITY)
  itk_fedisableexcept(FE_DIVBYZERO);
  itk_fedisableexcept(FE_INVALID);
  FloatingPointExceptions::m_PimplGlobals->m_Enabled = false;
#else
  itkFloatingPointExceptionsNotSupported();
#endif
}

bool
FloatingPointExceptions::HasFloatingPointExceptionsSupport()
{
  itkInitGlobalsMacro(PimplGlobals);
#if defined(ITK_HAS_FPE_CAPABILITY)
  return true;
#else
  return false;
#endif
}

} // namespace itk
<|endoftext|>