<commit_before>// Copyright 2015 FMAW

#include "./player_ai.h"

#include <cstdlib>
#include <vector>
#include <map>
#include <algorithm>

#include "./constants.h"
#include "./unit.h"
#include "./cell.h"
#include "./turnManager.h"

#include "./FMAW.h"

#include <algorithm>

PlayerAI::PlayerAI(Grid *grid, std::function<void(void)> callback) :
    grid(grid),
    onFinishTurnCallback(callback),
    seed(42) {}

PlayerAI::PlayerAI(Grid *grid, std::function<void(void)> call, PlayerID ID) :
    Player(ID),
    grid(grid),
    onFinishTurnCallback(call),
    seed(42) {}

void PlayerAI::startTurn() {

    // Prevent user from interacting with grid.
    this->grid->dequeueCallbacks();

    IndexPath previousPositionOfCursor = this->grid->getSelectedPath();

    bool recompute = true;
    std::map<IndexPath, int> distanceToNearestEnemyUnit;

    // Helper function to compute best cell to move.
    auto nearestEnemy = [&distanceToNearestEnemyUnit](IndexPath i, IndexPath j)
    -> bool {
        return distanceToNearestEnemyUnit[i] < distanceToNearestEnemyUnit[j];
    };

    // Now we have to make some decisions...
    // We will call the following callback once each 1.5s.
    this->unitNumber = 0;
    auto moveSomeUnit = [this, previousPositionOfCursor, &recompute,
    &distanceToNearestEnemyUnit, nearestEnemy](int ID) {
        FMAW::printf("Computing AI data...");
        // Here we will store the units of this AI player and the path where
        // they are located.
        std::vector<Unit *> IAunits;
        std::vector<IndexPath> IApaths;
        std::vector<Unit *> playerUnits;
        std::vector<IndexPath> playerPaths;
        std::vector<IndexPath> chosenPaths;

        // We have to check the full grid to know where are our units.
        for (int row = 0; row < this->grid->numRows(); row++) {
            for (int col = 0; col < this->grid->numCols(); col++)  {
                IndexPath path = { row, col };
                Cell *c = this->grid->cellAtIndexPath(path);
                if (c->isOccupied()) {
                    Unit *u = c->getCharacter();
                    if (u->getOwner() == this->ID) {
                        IAunits.push_back(u);
                        IApaths.push_back(path);
                    } else {
                        playerUnits.push_back(u);
                        playerPaths.push_back(path);
                    }
                }
            }
        }

        FMAW::printf("\tReady units");

        std::map<IndexPath, std::vector<IndexPath>> IAUnitCanAttack;
        std::map<IndexPath, std::vector<IndexPath>> PlayerUnitCanBeAttackedBy;

        // Compute which units can attack which ones.
        for (IndexPath p : IApaths) {
            this->grid->setPickedUpCell(p);
            Cell *c = this->grid->cellAtIndexPath(p);
            if (c->getCharacter()->hasAvailableActions()) {
                for (IndexPath t : playerPaths) {
                    if (grid->pickedUpUnitCanAttackCharacterAtCell(t)) {
                        IAUnitCanAttack[p].push_back(t);
                        PlayerUnitCanBeAttackedBy[t].push_back(p);
                    }
                }
            }
        }

        FMAW::printf("\tReady attacks");

        if (recompute) {
            // Compute distance to nearest visible enemy.
            for (int row = 0; row < this->grid->numRows(); row++) {
                for (int col = 0; col < this->grid->numCols(); col++) {
                    IndexPath f = { row, col };
                    Cell *from = grid->cellAtIndexPath(f);
                    distanceToNearestEnemyUnit[f] = COST_CELL_INFINITY;
                    for (int tr = 0; tr < this->grid->numRows(); tr++) {
                        for (int tc = 0; tc < this->grid->numCols(); tc++) {
                            IndexPath t = { tr, tc };
                            Cell *to = this->grid->cellAtIndexPath(t);
                            int distance = abs(t.row - f.row) +
                                           abs(t.col - f.col);
                            if (to->isOccupied()) {
                                Unit *u = to->getCharacter();
                                if (this->grid->canSeeCharacterAtCell(t) &&
                                        u->getOwner() != this->ID) {
                                    distanceToNearestEnemyUnit[f] = distance;
                                }
                            }
                        }
                    }
                }
            }
            recompute = false;
        }

        FMAW::printf("\tReady distances");

        bool canDoSomething = false;
        for (Unit *u : IAunits) {
            if (u->hasAvailableActions()) {
                canDoSomething = true;
            }
        }
        if (canDoSomething) {
            while (!IAunits[this->unitNumber]->hasAvailableActions()) {
                this->unitNumber++;
                this->unitNumber %= IAunits.size();
            }
            //------------------------------------------------------------------
            // Actual AI script.
            //------------------------------------------------------------------
            bool actionDone = false;
            IndexPath path = IApaths[this->unitNumber];
            Unit *unit = IAunits[this->unitNumber];
            this->grid->setPickedUpCell(path);

            int row = path.row;
            int col = path.col;

            FMAW::printf("Choosing action for unit %d at %d %d",
                         this->unitNumber, row, col);

            std::vector<IndexPath> shouldAttack;
            std::vector<IndexPath> attackable;

            // If I'm the only one who can attack, I'll do (random on tie).

            attackable = IAUnitCanAttack[path];
            for (IndexPath t : attackable) {
                if (PlayerUnitCanBeAttackedBy[t].size() == 1) {
                    shouldAttack.push_back(t);
                }
            }

            FMAW::printf("\tI'm the only one that can attack %d units",
                         attackable.size());

            // If I'm not the only one, I attack to a random (random on tie).
            if (shouldAttack.size() == 0) {
                attackable = IAUnitCanAttack[path];
                for (IndexPath t : attackable) {
                    shouldAttack.push_back(t);
                }

                FMAW::printf("\tMe and others can attack %d units",
                             attackable.size());
            }

            // I'll attack if I can.
            if (shouldAttack.size() > 0) {
                while (shouldAttack.size() > 0) {
                    int rand = rand_r(&(this->seed)) % shouldAttack.size();
                    IndexPath tg = shouldAttack[rand];
                    if (this->grid->pickedUpUnitCanAttackCharacterAtCell(tg)) {
                        FMAW::printf("\tWill attack unit at %d %d",
                                     tg.row, tg.col);
                        bool kill = this->grid->attackCharacterAtCell(
                                        path, tg, ATTACK_DURATION);
                        if (kill) {
                            recompute = true;
                        }
                        actionDone = true;
                        break;
                    } else {
                        FMAW::printf("\tCouldn't to attack unit at %d %d",
                                     tg.row, tg.col);
                        shouldAttack.erase(shouldAttack.begin() + rand);
                    }
                }
            }

            // If I can't attack, I'll move to the nearest enemy unit (random
            // on tie).
            if (!actionDone) {
                FMAW::printf("\tI couldn't attack any unit, so I'll move");
                std::vector<IndexPath> availableMovements;
                for (int r = 0; r < this->grid->numRows(); r++) {
                    for (int c = 0; c < this->grid->numCols(); c++) {
                        IndexPath t = { r, c };
                        if (this->grid->canMoveCharacterFromCellToCell(path, t)) {
                            availableMovements.push_back(t);
                        }
                    }
                }

                FMAW::printf("\tI can move to %d cells",
                             availableMovements.size());

                std::sort(availableMovements.begin(), availableMovements.end(),
                          nearestEnemy);

                if (availableMovements.size() > 0) {
                    IndexPath chosen = availableMovements[0];
                    FMAW::printf("\tI'll move to %d %d",
                                 chosen.row, chosen.col);
                    this->grid->moveCharacterFromCellToCell(path, chosen,
                                                            MOVEMENT_DURATION);
                    IApaths[this->unitNumber] = chosen;
                }
            }

            FMAW::printf("\t-------------------------------------------------");

            //------------------------------------------------------------------
            // End of AI script.
            //------------------------------------------------------------------
            this->grid->selectCellAtIndexPath(previousPositionOfCursor);
        } else {
            FMAW::printf("\tI've finished!");
            this->grid->enqueueCallbacks();
            FMAW::Timer::dequeue_function(ID);
            this->onFinishTurnCallback();
        }
    };
    FMAW::Timer::enqueue_function(moveSomeUnit, 1500, true);
}

void PlayerAI::print() {
    FMAW::printf("I'm an AI player with ID=%d", this->ID);
}
<commit_msg>Reversed performance improvements to ensure stability.<commit_after>// Copyright 2015 FMAW

#include "./player_ai.h"

#include <cstdlib>
#include <vector>
#include <map>
#include <algorithm>

#include "./constants.h"
#include "./unit.h"
#include "./cell.h"
#include "./turnManager.h"

#include "./FMAW.h"

#include <algorithm>

PlayerAI::PlayerAI(Grid *grid, std::function<void(void)> callback) :
    grid(grid),
    onFinishTurnCallback(callback),
    seed(42) {}

PlayerAI::PlayerAI(Grid *grid, std::function<void(void)> call, PlayerID ID) :
    Player(ID),
    grid(grid),
    onFinishTurnCallback(call),
    seed(42) {}

void PlayerAI::startTurn() {

    // Prevent user from interacting with grid.
    this->grid->dequeueCallbacks();

    IndexPath previousPositionOfCursor = this->grid->getSelectedPath();

    // Now we have to make some decisions...
    // We will call the following callback once each 1.5s.
    this->unitNumber = 0;
    auto moveSomeUnit = [this, previousPositionOfCursor](int ID) {
        // Here we will store the units of this AI player and the path where they
        // are located.
        std::vector<Unit *> IAunits;
        std::vector<IndexPath> IApaths;
        std::vector<Unit *> playerUnits;
        std::vector<IndexPath> playerPaths;
        std::vector<IndexPath> chosenPaths;

        // We have to check the full grid to know where are our units.
        for (int row = 0; row < this->grid->numRows(); row++) {
            for (int col = 0; col < this->grid->numCols(); col++)  {
                IndexPath path = { row, col };
                Cell *c = this->grid->cellAtIndexPath(path);
                if (c->isOccupied()) {
                    Unit *u = c->getCharacter();
                    if (u->getOwner() == this->ID) {
                        IAunits.push_back(u);
                        IApaths.push_back(path);
                    } else {
                        playerUnits.push_back(u);
                        playerPaths.push_back(path);
                    }
                }
            }
        }

        std::map<IndexPath, std::vector<IndexPath>> IAUnitCanAttack;
        std::map<IndexPath, std::vector<IndexPath>> PlayerUnitCanBeAttackedBy;

        std::map<IndexPath, std::vector<IndexPath>> IAUnitsPossibleMovements;
        std::map<IndexPath, int> distanceToNearestEnemyUnit;

        // Compute which units can attack which ones.
        for (IndexPath p : IApaths) {
            this->grid->setPickedUpCell(p);
            Cell *c = this->grid->cellAtIndexPath(p);
            if (c->getCharacter()->hasAvailableActions()) {
                for (IndexPath t : playerPaths) {
                    if (grid->pickedUpUnitCanAttackCharacterAtCell(t)) {
                        IAUnitCanAttack[p].push_back(t);
                        PlayerUnitCanBeAttackedBy[t].push_back(p);
                    }
                }
            }
        }

        // Compute where can be moved IA units.
        for (IndexPath p : IApaths) {
            for (int row = 0; row < this->grid->numRows(); row++) {
                for (int col = 0; col < this->grid->numCols(); col++)  {
                    IndexPath t = { row, col };
                    if (grid->canMoveCharacterFromCellToCell(p, t)) {
                        IAUnitsPossibleMovements[p].push_back(t);
                    }
                }
            }
        }

        // Compute distance to nearest visible enemy.
        for (int row = 0; row < this->grid->numRows(); row++) {
            for (int col = 0; col < this->grid->numCols(); col++) {
                IndexPath f = { row, col };
                Cell *from = grid->cellAtIndexPath(f);
                distanceToNearestEnemyUnit[f] = COST_CELL_INFINITY;
                for (int t_row = 0; t_row < this->grid->numRows(); t_row++) {
                    for (int t_col = 0; t_col < this->grid->numCols(); t_col++) {
                        IndexPath t = { t_row, t_col };
                        Cell *to = this->grid->cellAtIndexPath(t);
                        int distance = abs(t.row - f.row) + abs(t.col - f.col);
                        if (to->isOccupied()) {
                            Unit *u = to->getCharacter();
                            if (this->grid->canSeeCharacterAtCell(t) &&
                                    u->getOwner() != this->ID) {
                                distanceToNearestEnemyUnit[f] = distance;
                            }
                        }
                    }
                }
            }
        }

        // Helper function to compute best cell to move.
        auto nearestEnemy = [&distanceToNearestEnemyUnit](
                                IndexPath i, IndexPath j
        ) -> bool {
            return distanceToNearestEnemyUnit[i] < distanceToNearestEnemyUnit[j];
        };

        bool canDoSomething = false;
        for (Unit *u : IAunits) {
            if (u->hasAvailableActions()) {
                canDoSomething = true;
            }
        }
        if (canDoSomething) {
            while (!IAunits[this->unitNumber]->hasAvailableActions()) {
                this->unitNumber++;
                this->unitNumber %= IAunits.size();
            }
            //------------------------------------------------------------------
            // Actual AI script.
            //------------------------------------------------------------------
            bool actionDone = false;
            IndexPath path = IApaths[this->unitNumber];
            Unit *unit = IAunits[this->unitNumber];
            this->grid->setPickedUpCell(path);

            int row = path.row;
            int col = path.col;

            FMAW::printf("Choosing action for unit %d at %d %d",
                         this->unitNumber, row, col);

            std::vector<IndexPath> shouldAttack;
            std::vector<IndexPath> attackable;

            // If I'm the only one who can attack, I'll do (random on tie).

            attackable = IAUnitCanAttack[path];
            for (IndexPath t : attackable) {
                if (PlayerUnitCanBeAttackedBy[t].size() == 1) {
                    shouldAttack.push_back(t);
                }
            }

            FMAW::printf("\tI'm the only one that can attack %d units",
                         attackable.size());

            // If I'm not the only one, I attack to a random (random on tie).
            if (shouldAttack.size() == 0) {
                attackable = IAUnitCanAttack[path];
                for (IndexPath t : attackable) {
                    shouldAttack.push_back(t);
                }

                FMAW::printf("\tMe and others can attack %d units",
                             attackable.size());
            }

            // I'll attack if I can.
            if (shouldAttack.size() > 0) {
                while (shouldAttack.size() > 0) {
                    int rand = rand_r(&(this->seed)) % shouldAttack.size();
                    IndexPath tg = shouldAttack[rand];
                    if (this->grid->pickedUpUnitCanAttackCharacterAtCell(tg)) {
                        FMAW::printf("\tWill attack unit at %d %d",
                                     tg.row, tg.col);
                        this->grid->attackCharacterAtCell(path, tg,
                                                          ATTACK_DURATION);
                        actionDone = true;
                        break;
                    } else {
                        FMAW::printf("\tCouldn't to attack unit at %d %d",
                                     tg.row, tg.col);
                        shouldAttack.erase(shouldAttack.begin() + rand);
                    }
                }
            }

            // If I can't attack, I'll move to the nearest enemy unit (random
            // on tie).
            if (!actionDone) {
                FMAW::printf("\tI couldn't attack any unit, so I'll move");
                std::vector<IndexPath> availableMovements;
                for (int r = 0; r < this->grid->numRows(); r++) {
                    for (int c = 0; c < this->grid->numCols(); c++) {
                        IndexPath t = { r, c };
                        if (this->grid->canMoveCharacterFromCellToCell(path, t)) {
                            availableMovements.push_back(t);
                        }
                    }
                }

                FMAW::printf("\tI can move to %d cells",
                             availableMovements.size());

                std::sort(availableMovements.begin(), availableMovements.end(),
                          nearestEnemy);

                if (availableMovements.size() > 0) {
                    IndexPath chosen = availableMovements[0];
                    FMAW::printf("\tI'll move to %d %d",
                                 chosen.row, chosen.col);
                    this->grid->moveCharacterFromCellToCell(path, chosen,
                                                            MOVEMENT_DURATION);
                    IApaths[this->unitNumber] = chosen;
                }
            }

            FMAW::printf("\t-------------------------------------------------");

            //------------------------------------------------------------------
            // End of AI script.
            //------------------------------------------------------------------
            this->grid->selectCellAtIndexPath(previousPositionOfCursor);
        } else {
            FMAW::printf("\tI've finished!");
            this->grid->enqueueCallbacks();
            FMAW::Timer::dequeue_function(ID);
            this->onFinishTurnCallback();
        }
    };
    FMAW::Timer::enqueue_function(moveSomeUnit, 1500, true);
}

void PlayerAI::print() {
    FMAW::printf("I'm an AI player with ID=%d", this->ID);
}
<|endoftext|>


<commit_before>#include "p0compile/parser.hpp"
#include "p0compile/expression_tree.hpp"
#include "p0compile/statement_tree.hpp"
#include "p0compile/scanner.hpp"
#include "p0compile/compiler_error.hpp"
#include <boost/test/unit_test.hpp>

namespace
{
	bool handle_unexpected_error(p0::compiler_error const &error)
	{
		(void)error;
		BOOST_REQUIRE(nullptr == "No error expected");
		return true;
	}

	template <class ASTHandler>
	void test_parser(std::string const &source, ASTHandler const &handle_ast)
	{
		p0::source_range const source_range(
					source.data(),
					source.data() + source.size());
		p0::scanner scanner(source_range);
		p0::parser parser(scanner, handle_unexpected_error);
		auto const ast = parser.parse_unit();
		BOOST_REQUIRE(ast);
		handle_ast(*ast);
	}

	template <class Base>
	struct down_caster
	{
		down_caster(Base const &base)
			: m_base(&base)
		{
		}

		template <class Down>
		operator Down const &() const
		{
			assert(m_base);
			return dynamic_cast<Down const &>(*m_base);
		}

	private:

		Base const *m_base;
	};

	template <class Handler>
	void check_expression(p0::expression_tree const &expression, Handler const &handler)
	{
		handler(down_caster<p0::expression_tree>(expression));
	}

	template <class Handler>
	void check_statement(p0::statement_tree const &statement, Handler const &handler)
	{
		handler(down_caster<p0::statement_tree>(statement));
	}
}

BOOST_AUTO_TEST_CASE(parse_empty_file_test)
{
	test_parser("", [](p0::function_tree const &ast)
	{
		BOOST_CHECK(ast.parameters().empty());
		check_statement(ast.body(), [](p0::block_tree const &block)
		{
			BOOST_CHECK(block.body().empty());
		});
	});
}

BOOST_AUTO_TEST_CASE(parse_return_test)
{
	test_parser("return null", [](p0::function_tree const &ast)
	{
		BOOST_CHECK(ast.parameters().empty());
		check_statement(ast.body(), [](p0::block_tree const &block)
		{
			BOOST_REQUIRE(block.body().size() == 1);
			check_statement(*block.body()[0], [](p0::return_tree const &return_)
			{
				check_expression(return_.value(), [](p0::null_expression_tree const &)
				{
				});
			});
		});
	});
}

BOOST_AUTO_TEST_CASE(parse_load_module_test)
{
	test_parser("import name", [](p0::function_tree const &ast)
	{
		BOOST_CHECK(ast.parameters().empty());
		check_statement(ast.body(), [](p0::block_tree const &block)
		{
			BOOST_REQUIRE(block.body().size() == 1);
			check_statement(*block.body()[0], [](p0::expression_statement_tree const &statement)
			{
				check_expression(statement.expression(), [](p0::import_expression_tree const &load_module)
				{
					check_expression(load_module.name(), [](p0::name_expression_tree const &name)
					{
						BOOST_CHECK("name" == p0::source_range_to_string(name.name()));
					});
				});
			});
		});
	});
}
<commit_msg>test some expected parser errors<commit_after>#include "p0compile/parser.hpp"
#include "p0compile/expression_tree.hpp"
#include "p0compile/statement_tree.hpp"
#include "p0compile/scanner.hpp"
#include "p0compile/compiler_error.hpp"
#include <boost/test/unit_test.hpp>

namespace
{
	template <class ASTHandler, class ErrorHandler>
	void check_source(std::string const &source,
					  ASTHandler const &handle_ast,
					  ErrorHandler const &handle_error)
	{
		p0::source_range const source_range(
					source.data(),
					source.data() + source.size());
		p0::scanner scanner(source_range);
		p0::parser parser(scanner, handle_error);
		auto const ast = parser.parse_unit();
		BOOST_REQUIRE(ast);
		handle_ast(*ast);
	}

	bool handle_unexpected_error(p0::compiler_error const &error)
	{
		(void)error;
		BOOST_REQUIRE(nullptr == "No error expected");
		return true;
	}

	template <class ASTHandler>
	void check_valid_source(std::string const &source,
							ASTHandler const &handle_ast)
	{
		return check_source(source, handle_ast, handle_unexpected_error);
	}

	void check_invalid_source(std::string const &source,
							  std::size_t error_position)
	{
		bool has_failed = false;
		check_source(
			source,
			[](p0::function_tree const &)
		{
			//ignore results
		},
			[error_position, &source, &has_failed](p0::compiler_error const &error) -> bool
		{
			auto const error_found_at = error.position().begin();
			BOOST_CHECK(source.data() + error_position == error_found_at);
			has_failed = true;
			return true;
		});
		BOOST_CHECK(has_failed);
	}

	template <class Base>
	struct down_caster
	{
		down_caster(Base const &base)
			: m_base(&base)
		{
		}

		template <class Down>
		operator Down const &() const
		{
			assert(m_base);
			return dynamic_cast<Down const &>(*m_base);
		}

	private:

		Base const *m_base;
	};

	template <class Handler>
	void check_expression(p0::expression_tree const &expression, Handler const &handler)
	{
		handler(down_caster<p0::expression_tree>(expression));
	}

	template <class Handler>
	void check_statement(p0::statement_tree const &statement, Handler const &handler)
	{
		handler(down_caster<p0::statement_tree>(statement));
	}
}

BOOST_AUTO_TEST_CASE(parse_empty_file_test)
{
	check_valid_source("", [](p0::function_tree const &ast)
	{
		BOOST_CHECK(ast.parameters().empty());
		check_statement(ast.body(), [](p0::block_tree const &block)
		{
			BOOST_CHECK(block.body().empty());
		});
	});
}

BOOST_AUTO_TEST_CASE(parse_return_test)
{
	check_valid_source("return null", [](p0::function_tree const &ast)
	{
		BOOST_CHECK(ast.parameters().empty());
		check_statement(ast.body(), [](p0::block_tree const &block)
		{
			BOOST_REQUIRE(block.body().size() == 1);
			check_statement(*block.body()[0], [](p0::return_tree const &return_)
			{
				check_expression(return_.value(), [](p0::null_expression_tree const &)
				{
				});
			});
		});
	});
}

BOOST_AUTO_TEST_CASE(parse_load_module_test)
{
	check_valid_source("import name", [](p0::function_tree const &ast)
	{
		BOOST_CHECK(ast.parameters().empty());
		check_statement(ast.body(), [](p0::block_tree const &block)
		{
			BOOST_REQUIRE(block.body().size() == 1);
			check_statement(*block.body()[0], [](p0::expression_statement_tree const &statement)
			{
				check_expression(statement.expression(), [](p0::import_expression_tree const &load_module)
				{
					check_expression(load_module.name(), [](p0::name_expression_tree const &name)
					{
						BOOST_CHECK("name" == p0::source_range_to_string(name.name()));
					});
				});
			});
		});
	});
}

BOOST_AUTO_TEST_CASE(missing_expression_test)
{
	check_invalid_source("return ", 7);
	check_invalid_source("import ", 7);
	check_invalid_source("5 + ", 4);
	check_invalid_source("f((((***", 5);
}
<|endoftext|>


<commit_before><commit_msg>Update FTPClient.cpp<commit_after><|endoftext|>


<commit_before>/*
 *
 * Author: Jeffrey Leung
 * Last edited: 2015-09-19
 *
 * This C++ file contains the implementation of the Labyrinth class, which uses
 * the Room class to create a 2-d mapping for a game.
 *
 */

#include <cmath>
#include <iostream>
#include <stdexcept>

#include "../include/room_properties.hpp"
#include "../include/room.hpp"
#include "../include/coordinate.hpp"
#include "../include/labyrinth.hpp"

// CONSTRUCTOR/DESTRUCTOR:

// Parameterized constructor
// An exception is thrown if:
//   A size of 0 is given (invalid_argument)
Labyrinth::Labyrinth( unsigned int x_size, unsigned int y_size )
{
  if( x_size == 0 )
  {
    if( y_size == 0 )
    {
      throw std::invalid_argument( "Error: Labyrinth() was given empty x and "\
        "y sizes." );
    }
    else
    {
      throw std::invalid_argument( "Error: Labyrinth() was given an empty "\
        "x size." );
    }
  }
  else if( y_size == 0 )
  {
    throw std::invalid_argument( "Error: Labyrinth() was given an empty "\
      "y size." );
  }

  rooms_ = new Room*[ y_size ];
  for( unsigned int i = 0; i < y_size; ++i )
  {
    rooms_[i] = new Room[ x_size ];
  }

  x_size_ = x_size;
  y_size_ = y_size;
}

// Destructor
Labyrinth::~Labyrinth()
{
  for( unsigned int i = 0; i < y_size_; ++i )
  {
    delete [] rooms_[i];
  }
  delete [] rooms_;
}

// SETUP:

// This method connects two Rooms by breaking their walls.
// An exception is thrown if:
//   The Rooms are already connected (logic_error)
//   The Rooms are the same (logic_error)
//   The Rooms are not adjacent (logic_error)
//   One or both Rooms are outside the Labyrinth (invalid_argument)
void Labyrinth::ConnectRooms( Coordinate rm_1, Coordinate rm_2 )
{
  if( rm_1 == rm_2 )
  {
    throw std::logic_error( "Error: ConnectRooms() was given the same "\
      "coordinate for the two Rooms." );
  }

  else if( !WithinBounds(rm_1) || !WithinBounds(rm_2) )
  {
    if( !WithinBounds(rm_1) )
    {
      if( !WithinBounds(rm_2) )
      {
        throw std::invalid_argument( "Error: ConnectRooms() was given invalid "\
          "coordinates for both rm_1 and rm_2." );
      }
      else
      {
        throw std::invalid_argument( "Error: ConnectRooms() was given "\
          "an invalid coordinate for rm_1." );
      }
    }

    else
    {
      throw std::invalid_argument( "Error: ConnectRooms() was given an "\
        "invalid coordinate for rm_2." );
    }
  }

  else if( !IsAdjacent(rm_1, rm_2) )
  {
    throw std::logic_error( "Error: ConnectRooms() was given two coordinates "\
      "which are not adjacent, and therefore cannot be connected." );
  }

  unsigned int x_distance = rm_2.x - rm_1.x;
  unsigned int y_distance = rm_2.y - rm_1.y;

  Direction break_wall_1;
  Direction break_wall_2;

  if( x_distance == 0 )
  {
    if( y_distance > 0 )  // rm_1 is north of rm_2
    {
      break_wall_1 = Direction::kSouth;
      break_wall_2 = Direction::kNorth;
    }
    else  // rm_1 is south of rm_2
    {
      break_wall_1 = Direction::kNorth;
      break_wall_2 = Direction::kSouth;
    }
  }

  else if( y_distance == 0 )
  {
    if( x_distance > 0 )  // rm_1 is west of rm_2
    {
      break_wall_1 = Direction::kEast;
      break_wall_2 = Direction::kWest;
    }
    else  // rm_1 is east of rm_2
    {
      break_wall_1 = Direction::kWest;
      break_wall_2 = Direction::kEast;
    }
  }

  else
  {
    throw std::logic_error( "Error: ConnectRooms() called IsAdjacent(), "\
      "which should have evaluated to false, but did not." );
  }

  RoomAt(rm_1).BreakWall(break_wall_1);
  RoomAt(rm_2).BreakWall(break_wall_2);
  return;
}

// PRIVATE METHODS:

// This private method returns a reference to the Room at the given
// coordinate.
// An exception is thrown if:
//   The Room is outside the Labyrinth (invalid_argument)
Room& Labyrinth::RoomAt( Coordinate rm ) const
{
  if( !WithinBounds(rm) )
  {
    throw std::invalid_argument( "Error: RoomAt() was given an invalid "\
      "coordinate for rm." );
  }
  return rooms_[rm.x][rm.y];
}

// This private method returns true if the Room is within the bounds of
// the Labyrinth, and false otherwise.
bool Labyrinth::WithinBounds( Coordinate rm ) const
{
  return(    0 < rm.x    &&
          rm.x < x_size_ &&
             0 < rm.y    &&
          rm.y < y_size_ );
}

// This private method returns true if the two Rooms are adjacent, and
// false otherwise.
// An exception is thrown if:
//   One or both Rooms are outside the Labyrinth (invalid_argument)
//   The same Room is given twice (logic_error)
bool Labyrinth::IsAdjacent( Coordinate rm_1, Coordinate rm_2 ) const
{
  if( !WithinBounds(rm_1) )
  {
    if( !WithinBounds(rm_2) )
    {
      throw std::invalid_argument( "Error: IsAdjacent() was given invalid "\
      "coordinates for both rm_1 and rm_2." );
    }
    else
    {
      throw std::invalid_argument( "Error: IsAdjacent() was given an invalid "\
      "coordinate for rm_1." );
    }
  }
  else if( !WithinBounds(rm_2) )
  {
    throw std::invalid_argument( "Error: IsAdjacent() was given an invalid "\
      "coordinate for rm_2." );
  }

  if( rm_1 == rm_2 )
  {
    throw std::logic_error( "Error: IsAdjacent() was given the same "\
      "coordinate for the Rooms." );
  }

  unsigned int x_distance = abs( rm_1.x - rm_2.x );
  unsigned int y_distance = abs( rm_1.y - rm_2.y );

  if( ( x_distance == 0 && y_distance == 1 ) ||
      ( x_distance == 1 && y_distance == 0 ) )
  {
    return true;
  }
  return false;
}
<commit_msg>Implementing Laby DirectionCheck().<commit_after>/*
 *
 * Author: Jeffrey Leung
 * Last edited: 2015-09-19
 *
 * This C++ file contains the implementation of the Labyrinth class, which uses
 * the Room class to create a 2-d mapping for a game.
 *
 */

#include <cmath>
#include <iostream>
#include <stdexcept>

#include "../include/room_properties.hpp"
#include "../include/room.hpp"
#include "../include/coordinate.hpp"
#include "../include/labyrinth.hpp"

// CONSTRUCTOR/DESTRUCTOR:

// Parameterized constructor
// An exception is thrown if:
//   A size of 0 is given (invalid_argument)
Labyrinth::Labyrinth( unsigned int x_size, unsigned int y_size )
{
  if( x_size == 0 )
  {
    if( y_size == 0 )
    {
      throw std::invalid_argument( "Error: Labyrinth() was given empty x and "\
        "y sizes." );
    }
    else
    {
      throw std::invalid_argument( "Error: Labyrinth() was given an empty "\
        "x size." );
    }
  }
  else if( y_size == 0 )
  {
    throw std::invalid_argument( "Error: Labyrinth() was given an empty "\
      "y size." );
  }

  rooms_ = new Room*[ y_size ];
  for( unsigned int i = 0; i < y_size; ++i )
  {
    rooms_[i] = new Room[ x_size ];
  }

  x_size_ = x_size;
  y_size_ = y_size;
}

// Destructor
Labyrinth::~Labyrinth()
{
  for( unsigned int i = 0; i < y_size_; ++i )
  {
    delete [] rooms_[i];
  }
  delete [] rooms_;
}

// SETUP:

// This method connects two Rooms by breaking their walls.
// An exception is thrown if:
//   The Rooms are already connected (logic_error)
//   The Rooms are the same (logic_error)
//   The Rooms are not adjacent (logic_error)
//   One or both Rooms are outside the Labyrinth (invalid_argument)
void Labyrinth::ConnectRooms( Coordinate rm_1, Coordinate rm_2 )
{
  if( rm_1 == rm_2 )
  {
    throw std::logic_error( "Error: ConnectRooms() was given the same "\
      "coordinate for the two Rooms." );
  }

  else if( !WithinBounds(rm_1) || !WithinBounds(rm_2) )
  {
    if( !WithinBounds(rm_1) )
    {
      if( !WithinBounds(rm_2) )
      {
        throw std::invalid_argument( "Error: ConnectRooms() was given invalid "\
          "coordinates for both rm_1 and rm_2." );
      }
      else
      {
        throw std::invalid_argument( "Error: ConnectRooms() was given "\
          "an invalid coordinate for rm_1." );
      }
    }

    else
    {
      throw std::invalid_argument( "Error: ConnectRooms() was given an "\
        "invalid coordinate for rm_2." );
    }
  }

  else if( !IsAdjacent(rm_1, rm_2) )
  {
    throw std::logic_error( "Error: ConnectRooms() was given two coordinates "\
      "which are not adjacent, and therefore cannot be connected." );
  }

  unsigned int x_distance = rm_2.x - rm_1.x;
  unsigned int y_distance = rm_2.y - rm_1.y;

  Direction break_wall_1;
  Direction break_wall_2;

  if( x_distance == 0 )
  {
    if( y_distance > 0 )  // rm_1 is north of rm_2
    {
      break_wall_1 = Direction::kSouth;
      break_wall_2 = Direction::kNorth;
    }
    else  // rm_1 is south of rm_2
    {
      break_wall_1 = Direction::kNorth;
      break_wall_2 = Direction::kSouth;
    }
  }

  else if( y_distance == 0 )
  {
    if( x_distance > 0 )  // rm_1 is west of rm_2
    {
      break_wall_1 = Direction::kEast;
      break_wall_2 = Direction::kWest;
    }
    else  // rm_1 is east of rm_2
    {
      break_wall_1 = Direction::kWest;
      break_wall_2 = Direction::kEast;
    }
  }

  else
  {
    throw std::logic_error( "Error: ConnectRooms() called IsAdjacent(), "\
      "which should have evaluated to false, but did not." );
  }

  RoomAt(rm_1).BreakWall(break_wall_1);
  RoomAt(rm_2).BreakWall(break_wall_2);
  return;
}

// PLAY:

// This method returns the type of RoomBorder in the given direction.
// An exception is thrown if:
//   The Room is outside the Labyrinth (invalid_argument)
//   Direction d is kNone (invalid_argument)
RoomBorder Labyrinth::DirectionCheck( Coordinate rm, Direction d ) const
{
  if( !WithinBounds(rm) )
  {
    throw std::invalid_argument( "Error: DirectionCheck() was given a "\
      "Coordinate outside of the Labyrinth." );
  }
  else if( d == Direction::kNone )
  {
    throw std::invalid_argument( "Error: DirectionCheck() was given an "\
      "invalid direction (kNone)." );
  }

  return RoomAt(rm).DirectionCheck(d);
}

// PRIVATE METHODS:

// This private method returns a reference to the Room at the given
// coordinate.
// An exception is thrown if:
//   The Room is outside the Labyrinth (invalid_argument)
Room& Labyrinth::RoomAt( Coordinate rm ) const
{
  if( !WithinBounds(rm) )
  {
    throw std::invalid_argument( "Error: RoomAt() was given an invalid "\
      "coordinate for rm." );
  }
  return rooms_[rm.x][rm.y];
}

// This private method returns true if the Room is within the bounds of
// the Labyrinth, and false otherwise.
bool Labyrinth::WithinBounds( Coordinate rm ) const
{
  return(    0 < rm.x    &&
          rm.x < x_size_ &&
             0 < rm.y    &&
          rm.y < y_size_ );
}

// This private method returns true if the two Rooms are adjacent, and
// false otherwise.
// An exception is thrown if:
//   One or both Rooms are outside the Labyrinth (invalid_argument)
//   The same Room is given twice (logic_error)
bool Labyrinth::IsAdjacent( Coordinate rm_1, Coordinate rm_2 ) const
{
  if( !WithinBounds(rm_1) )
  {
    if( !WithinBounds(rm_2) )
    {
      throw std::invalid_argument( "Error: IsAdjacent() was given invalid "\
      "coordinates for both rm_1 and rm_2." );
    }
    else
    {
      throw std::invalid_argument( "Error: IsAdjacent() was given an invalid "\
      "coordinate for rm_1." );
    }
  }
  else if( !WithinBounds(rm_2) )
  {
    throw std::invalid_argument( "Error: IsAdjacent() was given an invalid "\
      "coordinate for rm_2." );
  }

  if( rm_1 == rm_2 )
  {
    throw std::logic_error( "Error: IsAdjacent() was given the same "\
      "coordinate for the Rooms." );
  }

  unsigned int x_distance = abs( rm_1.x - rm_2.x );
  unsigned int y_distance = abs( rm_1.y - rm_2.y );

  if( ( x_distance == 0 && y_distance == 1 ) ||
      ( x_distance == 1 && y_distance == 0 ) )
  {
    return true;
  }
  return false;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBitmapCache.h"
#include "SkMutex.h"
#include "SkPixelRef.h"
#include "SkTraceEvent.h"

//#define SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
//#define SK_TRACE_PIXELREF_LIFETIME

#ifdef SK_BUILD_FOR_WIN32
    // We don't have SK_BASE_MUTEX_INIT on Windows.

    // must be a power-of-2. undef to just use 1 mutex
    #define PIXELREF_MUTEX_RING_COUNT       32
    static SkBaseMutex gPixelRefMutexRing[PIXELREF_MUTEX_RING_COUNT];

#else
    static SkBaseMutex gPixelRefMutexRing[] = {
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,

        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,

        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,

        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
    };
    // must be a power-of-2. undef to just use 1 mutex
    #define PIXELREF_MUTEX_RING_COUNT SK_ARRAY_COUNT(gPixelRefMutexRing)

#endif

static SkBaseMutex* get_default_mutex() {
    static int32_t gPixelRefMutexRingIndex;

    SkASSERT(SkIsPow2(PIXELREF_MUTEX_RING_COUNT));

    // atomic_inc might be overkill here. It may be fine if once in a while
    // we hit a race-condition and two subsequent calls get the same index...
    int index = sk_atomic_inc(&gPixelRefMutexRingIndex);
    return &gPixelRefMutexRing[index & (PIXELREF_MUTEX_RING_COUNT - 1)];
}

///////////////////////////////////////////////////////////////////////////////
#include "SkNextID.h"

uint32_t SkNextID::ImageID() {
    static uint32_t gID = 0;
    uint32_t id;
    // Loop in case our global wraps around, as we never want to return a 0.
    do {
        id = sk_atomic_fetch_add(&gID, 2u) + 2;  // Never set the low bit.
    } while (0 == id);
    return id;
}

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

void SkPixelRef::setMutex(SkBaseMutex* mutex) {
    if (NULL == mutex) {
        mutex = get_default_mutex();
    }
    fMutex = mutex;
}

// just need a > 0 value, so pick a funny one to aid in debugging
#define SKPIXELREF_PRELOCKED_LOCKCOUNT     123456789

static SkImageInfo validate_info(const SkImageInfo& info) {
    SkAlphaType newAlphaType = info.alphaType();
    SkAssertResult(SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAlphaType));
    return info.makeAlphaType(newAlphaType);
}

#ifdef SK_TRACE_PIXELREF_LIFETIME
    static int32_t gInstCounter;
#endif

SkPixelRef::SkPixelRef(const SkImageInfo& info)
    : fInfo(validate_info(info))
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
    , fStableID(SkNextID::ImageID())
#endif

{
#ifdef SK_TRACE_PIXELREF_LIFETIME
    SkDebugf(" pixelref %d\n", sk_atomic_inc(&gInstCounter));
#endif
    this->setMutex(NULL);
    fRec.zero();
    fLockCount = 0;
    this->needsNewGenID();
    fMutability = kMutable;
    fPreLocked = false;
    fAddedToCache.store(false);
}

SkPixelRef::SkPixelRef(const SkImageInfo& info, SkBaseMutex* mutex)
    : fInfo(validate_info(info))
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
    , fStableID(SkNextID::ImageID())
#endif
{
#ifdef SK_TRACE_PIXELREF_LIFETIME
    SkDebugf(" pixelref %d\n", sk_atomic_inc(&gInstCounter));
#endif
    this->setMutex(mutex);
    fRec.zero();
    fLockCount = 0;
    this->needsNewGenID();
    fMutability = kMutable;
    fPreLocked = false;
    fAddedToCache.store(false);
}

SkPixelRef::~SkPixelRef() {
#ifndef SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
    SkASSERT(SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount || 0 == fLockCount);
#endif

#ifdef SK_TRACE_PIXELREF_LIFETIME
    SkDebugf("~pixelref %d\n", sk_atomic_dec(&gInstCounter) - 1);
#endif
    this->callGenIDChangeListeners();
}

void SkPixelRef::needsNewGenID() {
    fTaggedGenID.store(0);
    SkASSERT(!this->genIDIsUnique()); // This method isn't threadsafe, so the assert should be fine.
}

void SkPixelRef::cloneGenID(const SkPixelRef& that) {
    // This is subtle.  We must call that.getGenerationID() to make sure its genID isn't 0.
    uint32_t genID = that.getGenerationID();

    // Neither ID is unique any more.
    // (These & ~1u are actually redundant.  that.getGenerationID() just did it for us.)
    this->fTaggedGenID.store(genID & ~1u);
    that. fTaggedGenID.store(genID & ~1u);

    // This method isn't threadsafe, so these asserts should be fine.
    SkASSERT(!this->genIDIsUnique());
    SkASSERT(!that. genIDIsUnique());
}

static void validate_pixels_ctable(const SkImageInfo& info, const SkColorTable* ctable) {
    if (info.isEmpty()) {
        return; // can't require ctable if the dimensions are empty
    }
    if (kIndex_8_SkColorType == info.colorType()) {
        SkASSERT(ctable);
    } else {
        SkASSERT(NULL == ctable);
    }
}

void SkPixelRef::setPreLocked(void* pixels, size_t rowBytes, SkColorTable* ctable) {
    SkASSERT(pixels);
    validate_pixels_ctable(fInfo, ctable);
    // only call me in your constructor, otherwise fLockCount tracking can get
    // out of sync.
    fRec.fPixels = pixels;
    fRec.fColorTable = ctable;
    fRec.fRowBytes = rowBytes;
    fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT;
    fPreLocked = true;
}

// Increments fLockCount only on success
bool SkPixelRef::lockPixelsInsideMutex() {
    fMutex->assertHeld();

    if (1 == ++fLockCount) {
        SkASSERT(fRec.isZero());
        if (!this->onNewLockPixels(&fRec)) {
            fRec.zero();
            fLockCount -= 1;    // we return fLockCount unchanged if we fail.
            return false;
        }
    }
    if (fRec.fPixels) {
        validate_pixels_ctable(fInfo, fRec.fColorTable);
        return true;
    }
    return false;
}

// For historical reasons, we always inc fLockCount, even if we return false.
// It would be nice to change this (it seems), and only inc if we actually succeed...
bool SkPixelRef::lockPixels() {
    SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);

    if (!fPreLocked) {
        TRACE_EVENT_BEGIN0("skia", "SkPixelRef::lockPixelsMutex");
        SkAutoMutexAcquire  ac(*fMutex);
        TRACE_EVENT_END0("skia", "SkPixelRef::lockPixelsMutex");
        SkDEBUGCODE(int oldCount = fLockCount;)
        bool success = this->lockPixelsInsideMutex();
        // lockPixelsInsideMutex only increments the count if it succeeds.
        SkASSERT(oldCount + (int)success == fLockCount);

        if (!success) {
            // For compatibility with SkBitmap calling lockPixels, we still want to increment
            // fLockCount even if we failed. If we updated SkBitmap we could remove this oddity.
            fLockCount += 1;
            return false;
        }
    }
    if (fRec.fPixels) {
        validate_pixels_ctable(fInfo, fRec.fColorTable);
        return true;
    }
    return false;
}

bool SkPixelRef::lockPixels(LockRec* rec) {
    if (this->lockPixels()) {
        *rec = fRec;
        return true;
    }
    return false;
}

void SkPixelRef::unlockPixels() {
    SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);

    if (!fPreLocked) {
        SkAutoMutexAcquire  ac(*fMutex);

        SkASSERT(fLockCount > 0);
        if (0 == --fLockCount) {
            // don't call onUnlockPixels unless onLockPixels succeeded
            if (fRec.fPixels) {
                this->onUnlockPixels();
                fRec.zero();
            } else {
                SkASSERT(fRec.isZero());
            }
        }
    }
}

bool SkPixelRef::requestLock(const LockRequest& request, LockResult* result) {
    SkASSERT(result);
    if (request.fSize.isEmpty()) {
        return false;
    }
    // until we support subsets, we have to check this...
    if (request.fSize.width() != fInfo.width() || request.fSize.height() != fInfo.height()) {
        return false;
    }

    if (fPreLocked) {
        result->fUnlockProc = NULL;
        result->fUnlockContext = NULL;
        result->fCTable = fRec.fColorTable;
        result->fPixels = fRec.fPixels;
        result->fRowBytes = fRec.fRowBytes;
        result->fSize.set(fInfo.width(), fInfo.height());
    } else {
        SkAutoMutexAcquire  ac(*fMutex);
        if (!this->onRequestLock(request, result)) {
            return false;
        }
    }
    if (result->fPixels) {
        validate_pixels_ctable(fInfo, result->fCTable);
        return true;
    }
    return false;
}

bool SkPixelRef::lockPixelsAreWritable() const {
    return this->onLockPixelsAreWritable();
}

bool SkPixelRef::onLockPixelsAreWritable() const {
    return true;
}

uint32_t SkPixelRef::getGenerationID() const {
    uint32_t id = fTaggedGenID.load();
    if (0 == id) {
        uint32_t next = SkNextID::ImageID() | 1u;
        if (fTaggedGenID.compare_exchange(&id, next)) {
            id = next;  // There was no race or we won the race.  fTaggedGenID is next now.
        } else {
            // We lost a race to set fTaggedGenID. compare_exchange() filled id with the winner.
        }
        // We can't quite SkASSERT(this->genIDIsUnique()). It could be non-unique
        // if we got here via the else path (pretty unlikely, but possible).
    }
    return id & ~1u;  // Mask off bottom unique bit.
}

void SkPixelRef::addGenIDChangeListener(GenIDChangeListener* listener) {
    if (NULL == listener || !this->genIDIsUnique()) {
        // No point in tracking this if we're not going to call it.
        SkDELETE(listener);
        return;
    }
    *fGenIDChangeListeners.append() = listener;
}

// we need to be called *before* the genID gets changed or zerod
void SkPixelRef::callGenIDChangeListeners() {
    // We don't invalidate ourselves if we think another SkPixelRef is sharing our genID.
    if (this->genIDIsUnique()) {
        for (int i = 0; i < fGenIDChangeListeners.count(); i++) {
            fGenIDChangeListeners[i]->onChange();
        }

        // TODO: SkAtomic could add "old_value = atomic.xchg(new_value)" to make this clearer.
        if (fAddedToCache.load()) {
            SkNotifyBitmapGenIDIsStale(this->getGenerationID());
            fAddedToCache.store(false);
        }
    }
    // Listeners get at most one shot, so whether these triggered or not, blow them away.
    fGenIDChangeListeners.deleteAll();
}

void SkPixelRef::notifyPixelsChanged() {
#ifdef SK_DEBUG
    if (this->isImmutable()) {
        SkDebugf("========== notifyPixelsChanged called on immutable pixelref");
    }
#endif
    this->callGenIDChangeListeners();
    this->needsNewGenID();
    this->onNotifyPixelsChanged();
}

void SkPixelRef::changeAlphaType(SkAlphaType at) {
    *const_cast<SkImageInfo*>(&fInfo) = fInfo.makeAlphaType(at);
}

void SkPixelRef::setImmutable() {
    fMutability = kImmutable;
}

void SkPixelRef::setImmutableWithID(uint32_t genID) {
    /*
     *  We are forcing the genID to match an external value. The caller must ensure that this
     *  value does not conflict with other content.
     *
     *  One use is to force this pixelref's id to match an SkImage's id
     */
    fMutability = kImmutable;
    fTaggedGenID.store(genID);
}

void SkPixelRef::setTemporarilyImmutable() {
    SkASSERT(fMutability != kImmutable);
    fMutability = kTemporarilyImmutable;
}

void SkPixelRef::restoreMutability() {
    SkASSERT(fMutability != kImmutable);
    fMutability = kMutable;
}

bool SkPixelRef::readPixels(SkBitmap* dst, const SkIRect* subset) {
    return this->onReadPixels(dst, subset);
}

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

bool SkPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) {
    return false;
}

void SkPixelRef::onNotifyPixelsChanged() { }

SkData* SkPixelRef::onRefEncodedData() {
    return NULL;
}

bool SkPixelRef::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
                                 SkYUVColorSpace* colorSpace) {
    return false;
}

size_t SkPixelRef::getAllocatedSizeInBytes() const {
    return 0;
}

static void unlock_legacy_result(void* ctx) {
    SkPixelRef* pr = (SkPixelRef*)ctx;
    pr->unlockPixels();
    pr->unref();    // balancing the Ref in onRequestLoc
}

bool SkPixelRef::onRequestLock(const LockRequest& request, LockResult* result) {
    if (!this->lockPixelsInsideMutex()) {
        return false;
    }

    result->fUnlockProc = unlock_legacy_result;
    result->fUnlockContext = SkRef(this);   // this is balanced in our fUnlockProc
    result->fCTable = fRec.fColorTable;
    result->fPixels = fRec.fPixels;
    result->fRowBytes = fRec.fRowBytes;
    result->fSize.set(fInfo.width(), fInfo.height());
    return true;
}
<commit_msg>decrement lockcount if we failed to get pixels<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBitmapCache.h"
#include "SkMutex.h"
#include "SkPixelRef.h"
#include "SkTraceEvent.h"

//#define SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
//#define SK_TRACE_PIXELREF_LIFETIME

#ifdef SK_BUILD_FOR_WIN32
    // We don't have SK_BASE_MUTEX_INIT on Windows.

    // must be a power-of-2. undef to just use 1 mutex
    #define PIXELREF_MUTEX_RING_COUNT       32
    static SkBaseMutex gPixelRefMutexRing[PIXELREF_MUTEX_RING_COUNT];

#else
    static SkBaseMutex gPixelRefMutexRing[] = {
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,

        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,

        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,

        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
        SK_BASE_MUTEX_INIT, SK_BASE_MUTEX_INIT,
    };
    // must be a power-of-2. undef to just use 1 mutex
    #define PIXELREF_MUTEX_RING_COUNT SK_ARRAY_COUNT(gPixelRefMutexRing)

#endif

static SkBaseMutex* get_default_mutex() {
    static int32_t gPixelRefMutexRingIndex;

    SkASSERT(SkIsPow2(PIXELREF_MUTEX_RING_COUNT));

    // atomic_inc might be overkill here. It may be fine if once in a while
    // we hit a race-condition and two subsequent calls get the same index...
    int index = sk_atomic_inc(&gPixelRefMutexRingIndex);
    return &gPixelRefMutexRing[index & (PIXELREF_MUTEX_RING_COUNT - 1)];
}

///////////////////////////////////////////////////////////////////////////////
#include "SkNextID.h"

uint32_t SkNextID::ImageID() {
    static uint32_t gID = 0;
    uint32_t id;
    // Loop in case our global wraps around, as we never want to return a 0.
    do {
        id = sk_atomic_fetch_add(&gID, 2u) + 2;  // Never set the low bit.
    } while (0 == id);
    return id;
}

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

void SkPixelRef::setMutex(SkBaseMutex* mutex) {
    if (NULL == mutex) {
        mutex = get_default_mutex();
    }
    fMutex = mutex;
}

// just need a > 0 value, so pick a funny one to aid in debugging
#define SKPIXELREF_PRELOCKED_LOCKCOUNT     123456789

static SkImageInfo validate_info(const SkImageInfo& info) {
    SkAlphaType newAlphaType = info.alphaType();
    SkAssertResult(SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &newAlphaType));
    return info.makeAlphaType(newAlphaType);
}

#ifdef SK_TRACE_PIXELREF_LIFETIME
    static int32_t gInstCounter;
#endif

SkPixelRef::SkPixelRef(const SkImageInfo& info)
    : fInfo(validate_info(info))
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
    , fStableID(SkNextID::ImageID())
#endif

{
#ifdef SK_TRACE_PIXELREF_LIFETIME
    SkDebugf(" pixelref %d\n", sk_atomic_inc(&gInstCounter));
#endif
    this->setMutex(NULL);
    fRec.zero();
    fLockCount = 0;
    this->needsNewGenID();
    fMutability = kMutable;
    fPreLocked = false;
    fAddedToCache.store(false);
}

SkPixelRef::SkPixelRef(const SkImageInfo& info, SkBaseMutex* mutex)
    : fInfo(validate_info(info))
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
    , fStableID(SkNextID::ImageID())
#endif
{
#ifdef SK_TRACE_PIXELREF_LIFETIME
    SkDebugf(" pixelref %d\n", sk_atomic_inc(&gInstCounter));
#endif
    this->setMutex(mutex);
    fRec.zero();
    fLockCount = 0;
    this->needsNewGenID();
    fMutability = kMutable;
    fPreLocked = false;
    fAddedToCache.store(false);
}

SkPixelRef::~SkPixelRef() {
#ifndef SK_SUPPORT_LEGACY_UNBALANCED_PIXELREF_LOCKCOUNT
    SkASSERT(SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount || 0 == fLockCount);
#endif

#ifdef SK_TRACE_PIXELREF_LIFETIME
    SkDebugf("~pixelref %d\n", sk_atomic_dec(&gInstCounter) - 1);
#endif
    this->callGenIDChangeListeners();
}

void SkPixelRef::needsNewGenID() {
    fTaggedGenID.store(0);
    SkASSERT(!this->genIDIsUnique()); // This method isn't threadsafe, so the assert should be fine.
}

void SkPixelRef::cloneGenID(const SkPixelRef& that) {
    // This is subtle.  We must call that.getGenerationID() to make sure its genID isn't 0.
    uint32_t genID = that.getGenerationID();

    // Neither ID is unique any more.
    // (These & ~1u are actually redundant.  that.getGenerationID() just did it for us.)
    this->fTaggedGenID.store(genID & ~1u);
    that. fTaggedGenID.store(genID & ~1u);

    // This method isn't threadsafe, so these asserts should be fine.
    SkASSERT(!this->genIDIsUnique());
    SkASSERT(!that. genIDIsUnique());
}

static void validate_pixels_ctable(const SkImageInfo& info, const SkColorTable* ctable) {
    if (info.isEmpty()) {
        return; // can't require ctable if the dimensions are empty
    }
    if (kIndex_8_SkColorType == info.colorType()) {
        SkASSERT(ctable);
    } else {
        SkASSERT(NULL == ctable);
    }
}

void SkPixelRef::setPreLocked(void* pixels, size_t rowBytes, SkColorTable* ctable) {
    SkASSERT(pixels);
    validate_pixels_ctable(fInfo, ctable);
    // only call me in your constructor, otherwise fLockCount tracking can get
    // out of sync.
    fRec.fPixels = pixels;
    fRec.fColorTable = ctable;
    fRec.fRowBytes = rowBytes;
    fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT;
    fPreLocked = true;
}

// Increments fLockCount only on success
bool SkPixelRef::lockPixelsInsideMutex() {
    fMutex->assertHeld();

    if (1 == ++fLockCount) {
        SkASSERT(fRec.isZero());
        if (!this->onNewLockPixels(&fRec)) {
            fRec.zero();
            fLockCount -= 1;    // we return fLockCount unchanged if we fail.
            return false;
        }
    }
    if (fRec.fPixels) {
        validate_pixels_ctable(fInfo, fRec.fColorTable);
        return true;
    }
    // no pixels, so we failed (somehow)
    --fLockCount;
    return false;
}

// For historical reasons, we always inc fLockCount, even if we return false.
// It would be nice to change this (it seems), and only inc if we actually succeed...
bool SkPixelRef::lockPixels() {
    SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);

    if (!fPreLocked) {
        TRACE_EVENT_BEGIN0("skia", "SkPixelRef::lockPixelsMutex");
        SkAutoMutexAcquire  ac(*fMutex);
        TRACE_EVENT_END0("skia", "SkPixelRef::lockPixelsMutex");
        SkDEBUGCODE(int oldCount = fLockCount;)
        bool success = this->lockPixelsInsideMutex();
        // lockPixelsInsideMutex only increments the count if it succeeds.
        SkASSERT(oldCount + (int)success == fLockCount);

        if (!success) {
            // For compatibility with SkBitmap calling lockPixels, we still want to increment
            // fLockCount even if we failed. If we updated SkBitmap we could remove this oddity.
            fLockCount += 1;
            return false;
        }
    }
    if (fRec.fPixels) {
        validate_pixels_ctable(fInfo, fRec.fColorTable);
        return true;
    }
    return false;
}

bool SkPixelRef::lockPixels(LockRec* rec) {
    if (this->lockPixels()) {
        *rec = fRec;
        return true;
    }
    return false;
}

void SkPixelRef::unlockPixels() {
    SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);

    if (!fPreLocked) {
        SkAutoMutexAcquire  ac(*fMutex);

        SkASSERT(fLockCount > 0);
        if (0 == --fLockCount) {
            // don't call onUnlockPixels unless onLockPixels succeeded
            if (fRec.fPixels) {
                this->onUnlockPixels();
                fRec.zero();
            } else {
                SkASSERT(fRec.isZero());
            }
        }
    }
}

bool SkPixelRef::requestLock(const LockRequest& request, LockResult* result) {
    SkASSERT(result);
    if (request.fSize.isEmpty()) {
        return false;
    }
    // until we support subsets, we have to check this...
    if (request.fSize.width() != fInfo.width() || request.fSize.height() != fInfo.height()) {
        return false;
    }

    if (fPreLocked) {
        result->fUnlockProc = NULL;
        result->fUnlockContext = NULL;
        result->fCTable = fRec.fColorTable;
        result->fPixels = fRec.fPixels;
        result->fRowBytes = fRec.fRowBytes;
        result->fSize.set(fInfo.width(), fInfo.height());
    } else {
        SkAutoMutexAcquire  ac(*fMutex);
        if (!this->onRequestLock(request, result)) {
            return false;
        }
    }
    if (result->fPixels) {
        validate_pixels_ctable(fInfo, result->fCTable);
        return true;
    }
    return false;
}

bool SkPixelRef::lockPixelsAreWritable() const {
    return this->onLockPixelsAreWritable();
}

bool SkPixelRef::onLockPixelsAreWritable() const {
    return true;
}

uint32_t SkPixelRef::getGenerationID() const {
    uint32_t id = fTaggedGenID.load();
    if (0 == id) {
        uint32_t next = SkNextID::ImageID() | 1u;
        if (fTaggedGenID.compare_exchange(&id, next)) {
            id = next;  // There was no race or we won the race.  fTaggedGenID is next now.
        } else {
            // We lost a race to set fTaggedGenID. compare_exchange() filled id with the winner.
        }
        // We can't quite SkASSERT(this->genIDIsUnique()). It could be non-unique
        // if we got here via the else path (pretty unlikely, but possible).
    }
    return id & ~1u;  // Mask off bottom unique bit.
}

void SkPixelRef::addGenIDChangeListener(GenIDChangeListener* listener) {
    if (NULL == listener || !this->genIDIsUnique()) {
        // No point in tracking this if we're not going to call it.
        SkDELETE(listener);
        return;
    }
    *fGenIDChangeListeners.append() = listener;
}

// we need to be called *before* the genID gets changed or zerod
void SkPixelRef::callGenIDChangeListeners() {
    // We don't invalidate ourselves if we think another SkPixelRef is sharing our genID.
    if (this->genIDIsUnique()) {
        for (int i = 0; i < fGenIDChangeListeners.count(); i++) {
            fGenIDChangeListeners[i]->onChange();
        }

        // TODO: SkAtomic could add "old_value = atomic.xchg(new_value)" to make this clearer.
        if (fAddedToCache.load()) {
            SkNotifyBitmapGenIDIsStale(this->getGenerationID());
            fAddedToCache.store(false);
        }
    }
    // Listeners get at most one shot, so whether these triggered or not, blow them away.
    fGenIDChangeListeners.deleteAll();
}

void SkPixelRef::notifyPixelsChanged() {
#ifdef SK_DEBUG
    if (this->isImmutable()) {
        SkDebugf("========== notifyPixelsChanged called on immutable pixelref");
    }
#endif
    this->callGenIDChangeListeners();
    this->needsNewGenID();
    this->onNotifyPixelsChanged();
}

void SkPixelRef::changeAlphaType(SkAlphaType at) {
    *const_cast<SkImageInfo*>(&fInfo) = fInfo.makeAlphaType(at);
}

void SkPixelRef::setImmutable() {
    fMutability = kImmutable;
}

void SkPixelRef::setImmutableWithID(uint32_t genID) {
    /*
     *  We are forcing the genID to match an external value. The caller must ensure that this
     *  value does not conflict with other content.
     *
     *  One use is to force this pixelref's id to match an SkImage's id
     */
    fMutability = kImmutable;
    fTaggedGenID.store(genID);
}

void SkPixelRef::setTemporarilyImmutable() {
    SkASSERT(fMutability != kImmutable);
    fMutability = kTemporarilyImmutable;
}

void SkPixelRef::restoreMutability() {
    SkASSERT(fMutability != kImmutable);
    fMutability = kMutable;
}

bool SkPixelRef::readPixels(SkBitmap* dst, const SkIRect* subset) {
    return this->onReadPixels(dst, subset);
}

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

bool SkPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) {
    return false;
}

void SkPixelRef::onNotifyPixelsChanged() { }

SkData* SkPixelRef::onRefEncodedData() {
    return NULL;
}

bool SkPixelRef::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
                                 SkYUVColorSpace* colorSpace) {
    return false;
}

size_t SkPixelRef::getAllocatedSizeInBytes() const {
    return 0;
}

static void unlock_legacy_result(void* ctx) {
    SkPixelRef* pr = (SkPixelRef*)ctx;
    pr->unlockPixels();
    pr->unref();    // balancing the Ref in onRequestLoc
}

bool SkPixelRef::onRequestLock(const LockRequest& request, LockResult* result) {
    if (!this->lockPixelsInsideMutex()) {
        return false;
    }

    result->fUnlockProc = unlock_legacy_result;
    result->fUnlockContext = SkRef(this);   // this is balanced in our fUnlockProc
    result->fCTable = fRec.fColorTable;
    result->fPixels = fRec.fPixels;
    result->fRowBytes = fRec.fRowBytes;
    result->fSize.set(fInfo.width(), fInfo.height());
    return true;
}
<|endoftext|>


<commit_before>
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkTileGrid.h"

SkTileGrid::SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount, SkTileGridNextDatumFunctionPtr nextDatumFunction)
{
    fTileWidth = tileWidth;
    fTileHeight = tileHeight;
    fXTileCount = xTileCount;
    fYTileCount = yTileCount;
    fTileCount = fXTileCount * fYTileCount;
    fInsertionCount = 0;
    fGridBounds = SkIRect::MakeXYWH(0, 0, fTileWidth * fXTileCount, fTileHeight * fYTileCount);
    fNextDatumFunction = nextDatumFunction;
    fTileData = SkNEW_ARRAY(SkTDArray<void *>, fTileCount);
}

SkTileGrid::~SkTileGrid() {
    SkDELETE_ARRAY(fTileData);
}

SkTDArray<void *>& SkTileGrid::tile(int x, int y) {
    return fTileData[y * fXTileCount + x];
}

void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) {
    SkASSERT(!bounds.isEmpty());
    SkIRect dilatedBounds = bounds;
    dilatedBounds.outset(1,1); // Consideration for filtering and AA

    if (!SkIRect::Intersects(dilatedBounds, fGridBounds)) {
        return;
    }

    int minTileX = SkMax32(SkMin32(dilatedBounds.left() / fTileWidth, fXTileCount - 1), 0);
    int maxTileX = SkMax32(SkMin32(dilatedBounds.right() / fTileWidth, fXTileCount - 1), 0);
    int minTileY = SkMax32(SkMin32(dilatedBounds.top() / fTileHeight, fYTileCount -1), 0);
    int maxTileY = SkMax32(SkMin32(dilatedBounds.bottom() / fTileHeight, fYTileCount -1), 0);

    for (int x = minTileX; x <= maxTileX; x++) {
        for (int y = minTileY; y <= maxTileY; y++) {
            this->tile(x, y).push(data);
        }
    }
    fInsertionCount++;
}

void SkTileGrid::search(const SkIRect& query, SkTDArray<void*>* results) {
    // The +1/-1 is to compensate for the outset in applied SkCanvas::getClipBounds
    int tileStartX = (query.left() + 1) / fTileWidth;
    int tileEndX = (query.right() + fTileWidth - 1) / fTileWidth;
    int tileStartY = (query.top() + 1) / fTileHeight;
    int tileEndY = (query.bottom() + fTileHeight - 1) / fTileHeight;
    if (tileStartX >= fXTileCount || tileStartY >= fYTileCount || tileEndX <= 0 || tileEndY <= 0) {
        return; // query does not intersect the grid
    }
    // clamp to grid
    if (tileStartX < 0) tileStartX = 0;
    if (tileStartY < 0) tileStartY = 0;
    if (tileEndX > fXTileCount) tileEndX = fXTileCount;
    if (tileEndY > fYTileCount) tileEndY = fYTileCount;

    int queryTileCount = (tileEndX - tileStartX) * (tileEndY - tileStartY);
    if (queryTileCount == 1) {
        *results = this->tile(tileStartX, tileStartY);
    } else {
        results->reset();
        SkTDArray<int> curPositions;
        curPositions.setCount(queryTileCount);
        // Note: Reserving space for 1024 tile pointers on the stack. If the malloc
        // becomes a bottleneck, we may consider increasing that number.
        SkAutoSTArray<1024, SkTDArray<void *>*> storage(queryTileCount);
        SkTDArray<void *>** tileRange = storage.get();
        int tile = 0;
        for (int x = tileStartX; x < tileEndX; ++x) {
            for (int y = tileStartY; y < tileEndY; ++y) {
                tileRange[tile] = &this->tile(x, y);
                curPositions[tile] = tileRange[tile]->count() ? 0 : kTileFinished;
                ++tile;
            }
        }
        void *nextElement;
        while(NULL != (nextElement = fNextDatumFunction(tileRange, curPositions))) {
            results->push(nextElement);
        }
    }
}

void SkTileGrid::clear() {
    for (int i = 0; i < fTileCount; i++) {
        fTileData[i].reset();
    }
}

int SkTileGrid::getCount() const {
    return fInsertionCount;
}
<commit_msg>Improving comment in SkTileGrid::search<commit_after>
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkTileGrid.h"

SkTileGrid::SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount, SkTileGridNextDatumFunctionPtr nextDatumFunction)
{
    fTileWidth = tileWidth;
    fTileHeight = tileHeight;
    fXTileCount = xTileCount;
    fYTileCount = yTileCount;
    fTileCount = fXTileCount * fYTileCount;
    fInsertionCount = 0;
    fGridBounds = SkIRect::MakeXYWH(0, 0, fTileWidth * fXTileCount, fTileHeight * fYTileCount);
    fNextDatumFunction = nextDatumFunction;
    fTileData = SkNEW_ARRAY(SkTDArray<void *>, fTileCount);
}

SkTileGrid::~SkTileGrid() {
    SkDELETE_ARRAY(fTileData);
}

SkTDArray<void *>& SkTileGrid::tile(int x, int y) {
    return fTileData[y * fXTileCount + x];
}

void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) {
    SkASSERT(!bounds.isEmpty());
    SkIRect dilatedBounds = bounds;
    dilatedBounds.outset(1,1); // Consideration for filtering and AA

    if (!SkIRect::Intersects(dilatedBounds, fGridBounds)) {
        return;
    }

    int minTileX = SkMax32(SkMin32(dilatedBounds.left() / fTileWidth, fXTileCount - 1), 0);
    int maxTileX = SkMax32(SkMin32(dilatedBounds.right() / fTileWidth, fXTileCount - 1), 0);
    int minTileY = SkMax32(SkMin32(dilatedBounds.top() / fTileHeight, fYTileCount -1), 0);
    int maxTileY = SkMax32(SkMin32(dilatedBounds.bottom() / fTileHeight, fYTileCount -1), 0);

    for (int x = minTileX; x <= maxTileX; x++) {
        for (int y = minTileY; y <= maxTileY; y++) {
            this->tile(x, y).push(data);
        }
    }
    fInsertionCount++;
}

void SkTileGrid::search(const SkIRect& query, SkTDArray<void*>* results) {
    // The +1/-1 is to compensate for the outset in applied SkCanvas::getClipBounds
    int tileStartX = (query.left() + 1) / fTileWidth;
    int tileEndX = (query.right() + fTileWidth - 1) / fTileWidth;
    int tileStartY = (query.top() + 1) / fTileHeight;
    int tileEndY = (query.bottom() + fTileHeight - 1) / fTileHeight;
    if (tileStartX >= fXTileCount || tileStartY >= fYTileCount || tileEndX <= 0 || tileEndY <= 0) {
        return; // query does not intersect the grid
    }
    // clamp to grid
    if (tileStartX < 0) tileStartX = 0;
    if (tileStartY < 0) tileStartY = 0;
    if (tileEndX > fXTileCount) tileEndX = fXTileCount;
    if (tileEndY > fYTileCount) tileEndY = fYTileCount;

    int queryTileCount = (tileEndX - tileStartX) * (tileEndY - tileStartY);
    if (queryTileCount == 1) {
        *results = this->tile(tileStartX, tileStartY);
    } else {
        results->reset();
        SkTDArray<int> curPositions;
        curPositions.setCount(queryTileCount);
        // Note: Reserving space for 1024 tile pointers on the stack. If the
        // malloc becomes a bottleneck, we may consider increasing that number.
        // Typical large web page, say 2k x 16k, would require 512 tiles of
        // size 256 x 256 pixels.
        SkAutoSTArray<1024, SkTDArray<void *>*> storage(queryTileCount);
        SkTDArray<void *>** tileRange = storage.get();
        int tile = 0;
        for (int x = tileStartX; x < tileEndX; ++x) {
            for (int y = tileStartY; y < tileEndY; ++y) {
                tileRange[tile] = &this->tile(x, y);
                curPositions[tile] = tileRange[tile]->count() ? 0 : kTileFinished;
                ++tile;
            }
        }
        void *nextElement;
        while(NULL != (nextElement = fNextDatumFunction(tileRange, curPositions))) {
            results->push(nextElement);
        }
    }
}

void SkTileGrid::clear() {
    for (int i = 0; i < fTileCount; i++) {
        fTileData[i].reset();
    }
}

int SkTileGrid::getCount() const {
    return fInsertionCount;
}
<|endoftext|>


<commit_before>/*
 * Conway's Game of Life implementation with less than 1KB code.
 * Prototype is developed first with Arduino then ported to ATtiny84A.
 *
 * Description:
 * - cells are displayed on an 8x8 LED matrix
 * - initial setup is set through 2 pots (X and Y) and one button to select/unselect a cell
 * - starting/suspending the game is done by a second push button
 * - a 3rd pot allows speed tuning
 *
 * Circuit:
 * - MCU is connected to 2 chained 74HC595 SIPO
 * - First SIPO is connected to matrix columns through 8 330Ohm resistors
 * - Second SIPO is connected to matrix rows
 *
 * Wiring:
 * - on Arduino UNO:
 *   - D2 is an output connected to both SIPO clock pins
 *   - D3 is an output connected to both SIPO latch pins
 *   - D4 is an output connected to first SIPO serial data input
 *   - D0 is an input connected to the START/STOP button (connected itself to GND)
 *   - D7 is an input connected to the SELECT button (connected itself to GND)
 *   - A0 is an analog input connected to the ROW potentiometer
 *   - A1 is an analog input connected to the COLUMN potentiometer
 * - on ATtinyX4 based boards:
 *   - PAx is an output connected to both SIPO clock pins
 *   - PAx is an output connected to both SIPO latch pins
 *   - PA0 is an output connected to first SIPO serial data input
 *   - PA5 is an input connected to the START/STOP button (connected itself to GND)
 *   - PA4 is an input connected to the SELECT button (connected itself to GND)
 *   - A6 is an analog input connected to the ROW potentiometer
 *   - A7 is an analog input connected to the COLUMN potentiometer
 */

//TODO 2. Reuse left pot (row selection) to determine speed of game
//TODO 3. Optimize if needed (several leads: remove vectors, use GPIOR for neighbours, use bit-parallel calculation...)
//TODO 4. Update ATtiny84 pins for actual project boards (no more prototype)

#include <avr/interrupt.h>
#include <util/delay.h>

#include <fastarduino/time.hh>
#include <fastarduino/AnalogInput.hh>

#include "Multiplexer.hh"
#include "Button.hh"
#include "Game.hh"

#if defined(ARDUINO_UNO)
static constexpr const Board::DigitalPin CLOCK = Board::DigitalPin::D2;
static constexpr const Board::DigitalPin LATCH = Board::DigitalPin::D3;
static constexpr const Board::DigitalPin DATA = Board::DigitalPin::D4;

static constexpr const Board::AnalogPin ROW = Board::AnalogPin::A0;
static constexpr const Board::AnalogPin COLUMN = Board::AnalogPin::A1;

static constexpr const Board::DigitalPin SELECT = Board::DigitalPin::D5;
static constexpr const Board::DigitalPin START_STOP = Board::DigitalPin::D6;

#define HAS_TRACE 1
#elif defined (BREADBOARD_ATTINYX4)
static constexpr const Board::DigitalPin CLOCK = Board::DigitalPin::D0;
static constexpr const Board::DigitalPin LATCH = Board::DigitalPin::D1;
static constexpr const Board::DigitalPin DATA = Board::DigitalPin::D2;

static constexpr const Board::AnalogPin ROW = Board::AnalogPin::A3;
static constexpr const Board::AnalogPin COLUMN = Board::AnalogPin::A4;

static constexpr const Board::DigitalPin SELECT = Board::DigitalPin::D5;
static constexpr const Board::DigitalPin START_STOP = Board::DigitalPin::D6;
//TODO Find real PAx for latch and clock
//static constexpr const Board::DigitalPin CLOCK = Board::DigitalPin::DX;
//static constexpr const Board::DigitalPin LATCH = Board::DigitalPin::DX;
//static constexpr const Board::DigitalPin DATA = Board::DigitalPin::D0;
//
//static constexpr const Board::AnalogPin ROW = Board::AnalogPin::A7;
//static constexpr const Board::AnalogPin COLUMN = Board::AnalogPin::A6;
//
//static constexpr const Board::DigitalPin SELECT = Board::DigitalPin::D4;
//static constexpr const Board::DigitalPin START_STOP = Board::DigitalPin::D5;
#define HAS_TRACE 0
#else
#error "Current target is not yet supported!"
#endif

#if HAS_TRACE
#include <fastarduino/uart.hh>
USE_UATX0();

// Buffers for UART
static const uint8_t OUTPUT_BUFFER_SIZE = 128;
static char output_buffer[OUTPUT_BUFFER_SIZE];
static UATX<Board::USART::USART0> uatx{output_buffer};
FormattedOutput<OutputBuffer> trace = uatx.fout();
#else
#include <fastarduino/empty_streams.hh>
#endif

// Single port used by this circuit
static constexpr const Board::Port PORT = FastPinType<CLOCK>::PORT;

// Check at compile time that all pins are on the same port
static_assert(FastPinType<LATCH>::PORT == PORT, "LATCH must be on same port as CLOCK");
static_assert(FastPinType<DATA>::PORT == PORT, "DATA must be on same port as CLOCK");
static_assert(FastPinType<SELECT>::PORT == PORT, "SELECT must be on same port as CLOCK");
static_assert(FastPinType<START_STOP>::PORT == PORT, "START_STOP must be on same port as CLOCK");

// Timing constants
// Multiplexing is done one row every 2ms, ie 8 rows in 16ms
static constexpr const uint16_t REFRESH_PERIOD_US = 1000;
// Blinking LEDs are toggled every 20 times the display is fully refreshed (ie 20 x 8 x 2ms = 320ms)
static constexpr const uint16_t BLINKING_HALF_TIME_MS = 250;
static constexpr const uint16_t BLINKING_COUNTER = BLINKING_HALF_TIME_MS * 1000UL / REFRESH_PERIOD_US;
// Buttons debouncing is done on a duration of 20ms
static constexpr const uint16_t DEBOUNCE_TIME_MS = 20;
static constexpr const uint8_t DEBOUNCE_COUNTER = DEBOUNCE_TIME_MS * 1000UL / REFRESH_PERIOD_US;

static constexpr const uint16_t PROGRESS_PERIOD_MS = 2000;
static constexpr const uint16_t PROGRESS_COUNTER = PROGRESS_PERIOD_MS * 1000UL / REFRESH_PERIOD_US;

// Useful constants and types
using MULTIPLEXER = Matrix8x8Multiplexer<CLOCK, LATCH, DATA, BLINKING_COUNTER>;
static constexpr const uint8_t ROWS = MULTIPLEXER::ROWS;
static constexpr const uint8_t COLUMNS = MULTIPLEXER::COLUMNS;
using GAME = GameOfLife<ROWS, COLUMNS>;

// Calculate direction of pins (3 output, 2 input with pullups)
static constexpr const uint8_t ALL_DDR = MULTIPLEXER::DDR_MASK;
static constexpr const uint8_t BUTTONS_MASK = FastPinType<SELECT>::MASK | FastPinType<START_STOP>::MASK;
static constexpr const uint8_t ALL_PORT = MULTIPLEXER::PORT_MASK | BUTTONS_MASK;

static constexpr const uint8_t SMILEY[] =
{
	0B01100110,
	0B10011001,
	0B10000001,
	0B10000001,
	0B01000010,
	0B00100100,
	0B00011000,
	0B00000000
};

// OPEN POINTS/TODO
// - Improve (use templates) to allow larger matrix size (eg 16x8, 16x16)
// - Cleanify code with 2 functions, 1 setup, 1 game?
int main() __attribute__((OS_main));
int main()
{
	// Enable interrupts at startup time
	sei();

#if HAS_TRACE
	// Setup traces
	uatx.begin(57600);
	trace.width(0);
#endif

	// Initialize all pins (only one port)
	FastPort<PORT>{ALL_DDR, ALL_PORT};

	// Initialize Multiplexer
	MULTIPLEXER mux;

	Button<START_STOP, DEBOUNCE_COUNTER> stop;
	// Step #1: Initialize board with 1st generation
	//===============================================
	{
		Button<SELECT, DEBOUNCE_COUNTER> select;
		AnalogInput<ROW, Board::AnalogReference::AVCC, uint8_t> row_input;
		AnalogInput<COLUMN, Board::AnalogReference::AVCC, uint8_t> column_input;
		uint8_t row = 0;
		uint8_t col = 0;
		mux.blinks()[0] = _BV(0);
		while (true)
		{
			// Update selected cell
			mux.blinks()[row] = 0;
			row = row_input.sample() >> 5;
			col = column_input.sample() >> 5;
			mux.blinks()[row] = _BV(col);
			// Check button states
			if (stop.unique_press())
				break;
			if (select.unique_press())
				mux.data()[row] ^= _BV(col);
			mux.refresh(BlinkMode::BLINK_ALL_BLINKS);
			Time::delay_us(REFRESH_PERIOD_US);
		}
	}

	// Step #2: Start game
	//=====================
	{
		// Initialize game board
		GAME game{mux.data()};

		// Loop to refresh LED matrix and progress game to next generation
		uint16_t progress_counter = 0;
		bool pause = false;
		while (true)
		{
			mux.refresh(BlinkMode::NO_BLINK);
			Time::delay_us(REFRESH_PERIOD_US);
			if (stop.unique_press())
				pause = !pause;
			if (!pause && ++progress_counter == PROGRESS_COUNTER)
			{
				game.progress_game();
				progress_counter = 0;
				// Check if game is finished (ie no more live cell, or still life)
				if (game.is_empty())
				{
					// Load a smiley into the game
					for (uint8_t i = 0; i < sizeof(SMILEY)/sizeof(SMILEY[0]); ++i)
						mux.data()[i] = SMILEY[i];
					break;
				}
				if (game.is_still())
					break;
			}
		}
	}

	// Step #3: End game
	//===================
	// Here we just need to refresh content and blink it until reset
	Time::delay_ms(1000);
	while (true)
	{
		Time::delay_us(REFRESH_PERIOD_US);
		mux.refresh(BlinkMode::BLINK_ALL_DATA);
	}
	return 0;
}
<commit_msg>Conway small code cleanup for ATtiny.<commit_after>/*
 * Conway's Game of Life implementation with less than 1KB code.
 * Prototype is developed first with Arduino then ported to ATtiny84A.
 *
 * Description:
 * - cells are displayed on an 8x8 LED matrix
 * - initial setup is set through 2 pots (X and Y) and one button to select/unselect a cell
 * - starting/suspending the game is done by a second push button
 * - a 3rd pot allows speed tuning
 *
 * Circuit:
 * - MCU is connected to 2 chained 74HC595 SIPO
 * - First SIPO is connected to matrix columns through 8 330Ohm resistors
 * - Second SIPO is connected to matrix rows
 *
 * Wiring:
 * - on Arduino UNO:
 *   - D2 is an output connected to both SIPO clock pins
 *   - D3 is an output connected to both SIPO latch pins
 *   - D4 is an output connected to first SIPO serial data input
 *   - D0 is an input connected to the START/STOP button (connected itself to GND)
 *   - D7 is an input connected to the SELECT button (connected itself to GND)
 *   - A0 is an analog input connected to the ROW potentiometer
 *   - A1 is an analog input connected to the COLUMN potentiometer
 * - on ATtinyX4 based boards:
 *   - PAx is an output connected to both SIPO clock pins
 *   - PAx is an output connected to both SIPO latch pins
 *   - PA0 is an output connected to first SIPO serial data input
 *   - PA5 is an input connected to the START/STOP button (connected itself to GND)
 *   - PA4 is an input connected to the SELECT button (connected itself to GND)
 *   - A6 is an analog input connected to the ROW potentiometer
 *   - A7 is an analog input connected to the COLUMN potentiometer
 */

//TODO 2. Reuse left pot (row selection) to determine speed of game
//TODO 3. Optimize if needed (several leads: remove vectors, use GPIOR for neighbours)
//TODO 4. Update ATtiny84 pins for actual project boards (no more prototype)

#include <avr/interrupt.h>
#include <util/delay.h>

#include <fastarduino/time.hh>
#include <fastarduino/AnalogInput.hh>

#include "Multiplexer.hh"
#include "Button.hh"
#include "Game.hh"

#if defined(ARDUINO_UNO)
static constexpr const Board::DigitalPin CLOCK = Board::DigitalPin::D2;
static constexpr const Board::DigitalPin LATCH = Board::DigitalPin::D3;
static constexpr const Board::DigitalPin DATA = Board::DigitalPin::D4;

static constexpr const Board::AnalogPin ROW = Board::AnalogPin::A0;
static constexpr const Board::AnalogPin COLUMN = Board::AnalogPin::A1;

static constexpr const Board::DigitalPin SELECT = Board::DigitalPin::D5;
static constexpr const Board::DigitalPin START_STOP = Board::DigitalPin::D6;

#define HAS_TRACE 1
#elif defined (BREADBOARD_ATTINYX4)
static constexpr const Board::DigitalPin CLOCK = Board::DigitalPin::D0;
static constexpr const Board::DigitalPin LATCH = Board::DigitalPin::D1;
static constexpr const Board::DigitalPin DATA = Board::DigitalPin::D2;

static constexpr const Board::AnalogPin ROW = Board::AnalogPin::A3;
static constexpr const Board::AnalogPin COLUMN = Board::AnalogPin::A4;

static constexpr const Board::DigitalPin SELECT = Board::DigitalPin::D5;
static constexpr const Board::DigitalPin START_STOP = Board::DigitalPin::D6;
//TODO Find real PAx for latch and clock
//static constexpr const Board::DigitalPin CLOCK = Board::DigitalPin::DX;
//static constexpr const Board::DigitalPin LATCH = Board::DigitalPin::DX;
//static constexpr const Board::DigitalPin DATA = Board::DigitalPin::D0;
//
//static constexpr const Board::AnalogPin ROW = Board::AnalogPin::A7;
//static constexpr const Board::AnalogPin COLUMN = Board::AnalogPin::A6;
//
//static constexpr const Board::DigitalPin SELECT = Board::DigitalPin::D4;
//static constexpr const Board::DigitalPin START_STOP = Board::DigitalPin::D5;
#else
#error "Current target is not yet supported!"
#endif

#if HAS_TRACE
#include <fastarduino/uart.hh>
USE_UATX0();

// Buffers for UART
static const uint8_t OUTPUT_BUFFER_SIZE = 128;
static char output_buffer[OUTPUT_BUFFER_SIZE];
static UATX<Board::USART::USART0> uatx{output_buffer};
FormattedOutput<OutputBuffer> trace = uatx.fout();
#endif

// Single port used by this circuit
static constexpr const Board::Port PORT = FastPinType<CLOCK>::PORT;

// Check at compile time that all pins are on the same port
static_assert(FastPinType<LATCH>::PORT == PORT, "LATCH must be on same port as CLOCK");
static_assert(FastPinType<DATA>::PORT == PORT, "DATA must be on same port as CLOCK");
static_assert(FastPinType<SELECT>::PORT == PORT, "SELECT must be on same port as CLOCK");
static_assert(FastPinType<START_STOP>::PORT == PORT, "START_STOP must be on same port as CLOCK");

// Timing constants
// Multiplexing is done one row every 2ms, ie 8 rows in 16ms
static constexpr const uint16_t REFRESH_PERIOD_US = 1000;
// Blinking LEDs are toggled every 20 times the display is fully refreshed (ie 20 x 8 x 2ms = 320ms)
static constexpr const uint16_t BLINKING_HALF_TIME_MS = 250;
static constexpr const uint16_t BLINKING_COUNTER = BLINKING_HALF_TIME_MS * 1000UL / REFRESH_PERIOD_US;
// Buttons debouncing is done on a duration of 20ms
static constexpr const uint16_t DEBOUNCE_TIME_MS = 20;
static constexpr const uint8_t DEBOUNCE_COUNTER = DEBOUNCE_TIME_MS * 1000UL / REFRESH_PERIOD_US;

static constexpr const uint16_t PROGRESS_PERIOD_MS = 2000;
static constexpr const uint16_t PROGRESS_COUNTER = PROGRESS_PERIOD_MS * 1000UL / REFRESH_PERIOD_US;

// Useful constants and types
using MULTIPLEXER = Matrix8x8Multiplexer<CLOCK, LATCH, DATA, BLINKING_COUNTER>;
static constexpr const uint8_t ROWS = MULTIPLEXER::ROWS;
static constexpr const uint8_t COLUMNS = MULTIPLEXER::COLUMNS;
using GAME = GameOfLife<ROWS, COLUMNS>;

// Calculate direction of pins (3 output, 2 input with pullups)
static constexpr const uint8_t ALL_DDR = MULTIPLEXER::DDR_MASK;
static constexpr const uint8_t BUTTONS_MASK = FastPinType<SELECT>::MASK | FastPinType<START_STOP>::MASK;
static constexpr const uint8_t ALL_PORT = MULTIPLEXER::PORT_MASK | BUTTONS_MASK;

static constexpr const uint8_t SMILEY[] =
{
	0B01100110,
	0B10011001,
	0B10000001,
	0B10000001,
	0B01000010,
	0B00100100,
	0B00011000,
	0B00000000
};

// OPEN POINTS/TODO
// - Improve (use templates) to allow larger matrix size (eg 16x8, 16x16)
// - Cleanify code with 2 functions, 1 setup, 1 game?
int main() __attribute__((OS_main));
int main()
{
	// Enable interrupts at startup time
	sei();

#if HAS_TRACE
	// Setup traces
	uatx.begin(57600);
	trace.width(0);
#endif

	// Initialize all pins (only one port)
	FastPort<PORT>{ALL_DDR, ALL_PORT};

	// Initialize Multiplexer
	MULTIPLEXER mux;

	Button<START_STOP, DEBOUNCE_COUNTER> stop;
	// Step #1: Initialize board with 1st generation
	//===============================================
	{
		Button<SELECT, DEBOUNCE_COUNTER> select;
		AnalogInput<ROW, Board::AnalogReference::AVCC, uint8_t> row_input;
		AnalogInput<COLUMN, Board::AnalogReference::AVCC, uint8_t> column_input;
		uint8_t row = 0;
		uint8_t col = 0;
		mux.blinks()[0] = _BV(0);
		while (true)
		{
			// Update selected cell
			mux.blinks()[row] = 0;
			row = row_input.sample() >> 5;
			col = column_input.sample() >> 5;
			mux.blinks()[row] = _BV(col);
			// Check button states
			if (stop.unique_press())
				break;
			if (select.unique_press())
				mux.data()[row] ^= _BV(col);
			mux.refresh(BlinkMode::BLINK_ALL_BLINKS);
			Time::delay_us(REFRESH_PERIOD_US);
		}
	}

	// Step #2: Start game
	//=====================
	{
		// Initialize game board
		GAME game{mux.data()};

		// Loop to refresh LED matrix and progress game to next generation
		uint16_t progress_counter = 0;
		bool pause = false;
		while (true)
		{
			mux.refresh(BlinkMode::NO_BLINK);
			Time::delay_us(REFRESH_PERIOD_US);
			if (stop.unique_press())
				pause = !pause;
			if (!pause && ++progress_counter == PROGRESS_COUNTER)
			{
				game.progress_game();
				progress_counter = 0;
				// Check if game is finished (ie no more live cell, or still life)
				if (game.is_empty())
				{
					// Load a smiley into the game
					for (uint8_t i = 0; i < sizeof(SMILEY)/sizeof(SMILEY[0]); ++i)
						mux.data()[i] = SMILEY[i];
					break;
				}
				if (game.is_still())
					break;
			}
		}
	}

	// Step #3: End game
	//===================
	// Here we just need to refresh content and blink it until reset
	Time::delay_ms(1000);
	while (true)
	{
		Time::delay_us(REFRESH_PERIOD_US);
		mux.refresh(BlinkMode::BLINK_ALL_DATA);
	}
	return 0;
}
<|endoftext|>