blob: 02d5195ecf22446fe419eea00dc468205d0fd294 [file] [log] [blame]
// Copyright 2006 Google Inc.
// All Rights Reserved.
// Author: <renn@google.com> (Marius Renn)
//
// Local includes
#include "debugging.h"
#include "helium_image.h"
#include "maxtracer.h"
#include "mathfunctions.h"
#include "trace.h"
// C includes
#include <stdlib.h>
using namespace helium;
// Constants -------------------------------------------------------------------
// Neighborhood map 0, 1, 2, 3, 4, 5, 6, 7
const int8 kNeighborX[8] = { 1, 1, 0, -1, -1, -1, 0, 1 };
const int8 kNeighborY[8] = { 0, 1, 1, 1, 0, -1, -1, -1 };
const int8 kMoveForOffset[3][3] = { { 5, 6, 7 },
{ 4, 8, 0 },
{ 3, 2, 1 } };
const unsigned kMaxNoise = 8;
const uint8 kNoLastMove = 8;
// Helper functions ------------------------------------------------------------
inline uint8 InvertMove(uint8 move) {
return (move + 4) % 8;
}
inline uint8 Mod8(int8 m) {
return (m < 0) ? (m + 8) : (m % 8);
}
// MaxTracer implementation ----------------------------------------------------
MaxTracer::MaxTracer(uint8 min_value)
: Tracer(), min_value_(min_value) {
}
bool MaxTracer::CanBeginTraceAt(const Point& p, const GrayMap& activation) {
if ((p.x <= 1) || (p.x >= (activation.width() - 1))) return false;
if ((p.y <= 1) || (p.y >= (activation.height() - 1))) return false;
uint8* edge_ptr = activation.Access(p);
unsigned width = activation.width();
bool is_h_max = (*(edge_ptr - 1) <= *edge_ptr)
&& (*(edge_ptr + 1) <= *edge_ptr);
bool is_v_max = (*(edge_ptr - width) <= *edge_ptr)
&& (*(edge_ptr + width) <= *edge_ptr);
if (is_h_max && is_v_max) {
trace_stack_.Clear();
return true;
}
return false;
}
bool MaxTracer::OutOfBounds(int x, int y) const {
return (x <= 0) || (y <= 0)
|| (x >= trace_map_->width() - 1) || (y >= trace_map_->height() - 1);
}
uint8 MoveDistance(uint8 m1, uint8 m2) {
// NOTE: Better way to do this???
if (m2 > m1)
return Min(m2 - m1, (m1 + 8) - m2);
else
return Min(m1 - m2, (m2 + 8) - m1);
}
bool MaxTracer::TraceEdgeAt(const Point& start, Trace& trace, uint8 traceid) {
ASSERT(trace_stack_.Empty());
if (OutOfBounds(start.x, start.y)) return false;
const GrayMap& edgemap = static_cast<const GrayMap&>(*trace_map_);
uint8 brightness[8];
Point p = start;
uint8* trace_ptr = edgemap.Access(p);
Color* history_ptr = scrap_->Access(p);
unsigned noise = 0;
uint8 last_move = kNoLastMove;
while(*trace_ptr > 0) {
uint8 best_move = 8;;
// Are we crossing another outline?
uint8 trace_under = Alpha(*history_ptr);
if (trace_under) {
if (trace_under == 0x01)
return false; // Ran into complete trace
else if (trace_under != traceid) {
noise++; // Yes, -> increase noise level
if (noise > kMaxNoise) return false;
}
}
// Back at start?
if ((abs(start.x - p.x) <= 1)
&& (abs(start.y - p.y) <= 1)
&& (trace.size() > 8)) {
trace.Add(kMoveForOffset[(start.y - p.y) + 1][(start.x - p.x) + 1]);
return true;
}
// Find best next move
uint8 max_value= 0;
for (uint8 d = 0; d < 8; ++d) {
// Check if the distance to the last move code is less than 1
if ((MoveDistance(last_move, d) > 1) && (last_move != 8))
brightness[d] = 0;
// Check if the next move would be within the map bounds, and the value
// there would be atleast the minimum threshold:
else if ((!OutOfBounds(p.x + kNeighborX[d], p.y + kNeighborY[d]))
&& (*(trace_ptr + neighbor_[d]) >= min_value_)) {
brightness[d] = *(trace_ptr + neighbor_[d]);
if (brightness[d] > max_value) {
max_value = brightness[d];
best_move = d;
}
} else brightness[d] = 0;
}
if ((best_move != 8) && (trace_under != traceid)) {
// Push alternatives onto stack
for (uint8 m = 0; m < 8; m++) {
if (abs(brightness[m] - max_value) < 24) {
TraceLoc loc(p, m, trace.size());
trace_stack_.Push(loc);
if (trace_stack_.Size() > 4800) return false;
}
}
} else {
// At dead end: Pop valid alternative off of stack
do {
if (trace_stack_.Empty()) return false;
// Pop trace location from stack and continue from there
TraceLoc loc = trace_stack_.Pop();
trace.RollBackTo(loc.index);
p = loc.point;
best_move = loc.move;
trace_ptr = edgemap.Access(p);
history_ptr = scrap_->Access(p);
} while (Alpha(*(history_ptr + neighbor_[best_move])) == traceid);
}
trace.Add(best_move);
//LOG(ERROR) << "Moving " << (int)best_move << " (" << p.x << ", "
// << p.y << ")";
// Mark this spot
SetAlphaAt(history_ptr, traceid);
// Move on
p.x += kNeighborX[best_move];
p.y += kNeighborY[best_move];
trace_ptr += neighbor_[best_move];
history_ptr += neighbor_[best_move];
last_move = best_move;
}
return false;
}