aboutsummaryrefslogtreecommitdiff
path: root/helium/maxtracer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'helium/maxtracer.cpp')
-rw-r--r--helium/maxtracer.cpp169
1 files changed, 169 insertions, 0 deletions
diff --git a/helium/maxtracer.cpp b/helium/maxtracer.cpp
new file mode 100644
index 0000000..789c58a
--- /dev/null
+++ b/helium/maxtracer.cpp
@@ -0,0 +1,169 @@
+// Copyright 2006 Google Inc.
+// All Rights Reserved.
+// Author: <renn@google.com> (Marius Renn)
+//
+// Local includes
+#include "debugging.h"
+#include "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;
+}