<commit_before>/* Suffix Array, copied. */

#define MAXN    (200010)
namespace mzry_sa
{
    int wx[MAXN],wy[MAXN],*x,*y,wss[MAXN],wv[MAXN];

    bool dacmp(int *r,int n,int a,int b,int l) {
        return a+l<n && b+l<n && r[a]==r[b]&&r[a+l]==r[b+l];
    }
    void da(int str[],int sa[],int rank[],int height[],int n,int m) {
        int *s = str;
        int *x=wx,*y=wy,*t,p;
        int i,j;
        for(i=0; i<m; i++)wss[i]=0;
        for(i=0; i<n; i++)wss[x[i]=s[i]]++;
        for(i=1; i<m; i++)wss[i]+=wss[i-1];
        for(i=n-1; i>=0; i--)sa[--wss[x[i]]]=i;
        for(j=1,p=1; p<n && j<n; j*=2,m=p) {
            for(i=n-j,p=0; i<n; i++)y[p++]=i;
            for(i=0; i<n; i++)if(sa[i]-j>=0)y[p++]=sa[i]-j;
            for(i=0; i<n; i++)wv[i]=x[y[i]];
            for(i=0; i<m; i++)wss[i]=0;
            for(i=0; i<n; i++)wss[wv[i]]++;
            for(i=1; i<m; i++)wss[i]+=wss[i-1];
            for(i=n-1; i>=0; i--)sa[--wss[wv[i]]]=y[i];
            for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0; i<n; i++)
            x[sa[i]]=dacmp(y,n,sa[i-1],sa[i],j)?p-1:p++;
        }
        for(int i=0; i<n; i++) rank[sa[i]]=i;
        for(int i=0,j=0,k=0; i<n; height[rank[i++]]=k)
            if(rank[i]>0)
            for(k?k--:0,j=sa[rank[i]-1];
                i+k < n && j+k < n && str[i+k]==str[j+k];
                k++);
    }
}

/*
Suffix array O(n lg^2 n)
LCP table O(n)
*/
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

#define REP(i, n) for (int i = 0; i < (int)(n); ++i)

namespace SuffixArray
{
	const int MAXN = 1 << 21;
	char * S;
	int N, gap;
	int sa[MAXN], pos[MAXN], tmp[MAXN], lcp[MAXN];

	bool sufCmp(int i, int j)
	{
		if (pos[i] != pos[j])
			return pos[i] < pos[j];
		i += gap;
		j += gap;
		return (i < N && j < N) ? pos[i] < pos[j] : i > j;
	}

	void buildSA()
	{
		N = strlen(S);
		REP(i, N) sa[i] = i, pos[i] = S[i];
		for (gap = 1;; gap *= 2)
		{
			sort(sa, sa + N, sufCmp);
			REP(i, N - 1) tmp[i + 1] = tmp[i] + sufCmp(sa[i], sa[i + 1]);
			REP(i, N) pos[sa[i]] = tmp[i];
			if (tmp[N - 1] == N - 1) break;
		}
	}

	void buildLCP()
	{
		for (int i = 0, k = 0; i < N; ++i) if (pos[i] != N - 1)
		{
			for (int j = sa[pos[i] + 1]; S[i + k] == S[j + k];)
			++k;
			lcp[pos[i]] = k;
			if (k)--k;
		}
	}
} // end namespace SuffixArray

namespace HashSuffixArray
{
	const int
		MAXN = 1 << 21;

	typedef unsigned long long hash;

	const hash BASE = 137;

	int N;
	char * S;
	int sa[MAXN];
	hash h[MAXN], hPow[MAXN];

	#define getHash(lo, size) (h[lo] - h[(lo) + (size)] * hPow[size])

	inline bool sufCmp(int i, int j)
	{
		int lo = 1, hi = min(N - i, N - j);
		while (lo <= hi)
		{
			int mid = (lo + hi) >> 1;
			if (getHash(i, mid) == getHash(j, mid))
				lo = mid + 1;
			else
				hi = mid - 1;
		}
		return S[i + hi] < S[j + hi];
	}

	void buildSA()
	{
		N = strlen(S);
		hPow[0] = 1;
		for (int i = 1; i <= N; ++i)
			hPow[i] = hPow[i - 1] * BASE;
		h[N] = 0;
		for (int i = N - 1; i >= 0; --i)
			h[i] = h[i + 1] * BASE + S[i], sa[i] = i;

		stable_sort(sa, sa + N, sufCmp);
	}

} // end namespace HashSuffixArray
<commit_msg>lrjs sa<commit_after>/* Suffix Array, copied. */

#define MAXN    (200010)
namespace mzry_sa
{
    int wx[MAXN],wy[MAXN],*x,*y,wss[MAXN],wv[MAXN];

    bool dacmp(int *r,int n,int a,int b,int l) {
        return a+l<n && b+l<n && r[a]==r[b]&&r[a+l]==r[b+l];
    }
    void da(int str[],int sa[],int rank[],int height[],int n,int m) {
        int *s = str;
        int *x=wx,*y=wy,*t,p;
        int i,j;
        for(i=0; i<m; i++)wss[i]=0;
        for(i=0; i<n; i++)wss[x[i]=s[i]]++;
        for(i=1; i<m; i++)wss[i]+=wss[i-1];
        for(i=n-1; i>=0; i--)sa[--wss[x[i]]]=i;
        for(j=1,p=1; p<n && j<n; j*=2,m=p) {
            for(i=n-j,p=0; i<n; i++)y[p++]=i;
            for(i=0; i<n; i++)if(sa[i]-j>=0)y[p++]=sa[i]-j;
            for(i=0; i<n; i++)wv[i]=x[y[i]];
            for(i=0; i<m; i++)wss[i]=0;
            for(i=0; i<n; i++)wss[wv[i]]++;
            for(i=1; i<m; i++)wss[i]+=wss[i-1];
            for(i=n-1; i>=0; i--)sa[--wss[wv[i]]]=y[i];
            for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0; i<n; i++)
            x[sa[i]]=dacmp(y,n,sa[i-1],sa[i],j)?p-1:p++;
        }
        for(int i=0; i<n; i++) rank[sa[i]]=i;
        for(int i=0,j=0,k=0; i<n; height[rank[i++]]=k)
            if(rank[i]>0)
            for(k?k--:0,j=sa[rank[i]-1];
                i+k < n && j+k < n && str[i+k]==str[j+k];
                k++);
    }
}

/*
Suffix array O(n lg^2 n)
LCP table O(n)
*/
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

#define REP(i, n) for (int i = 0; i < (int)(n); ++i)

namespace SuffixArray
{
	const int MAXN = 1 << 21;
	char * S;
	int N, gap;
	int sa[MAXN], pos[MAXN], tmp[MAXN], lcp[MAXN];

	bool sufCmp(int i, int j)
	{
		if (pos[i] != pos[j])
			return pos[i] < pos[j];
		i += gap;
		j += gap;
		return (i < N && j < N) ? pos[i] < pos[j] : i > j;
	}

	void buildSA()
	{
		N = strlen(S);
		REP(i, N) sa[i] = i, pos[i] = S[i];
		for (gap = 1;; gap *= 2)
		{
			sort(sa, sa + N, sufCmp);
			REP(i, N - 1) tmp[i + 1] = tmp[i] + sufCmp(sa[i], sa[i + 1]);
			REP(i, N) pos[sa[i]] = tmp[i];
			if (tmp[N - 1] == N - 1) break;
		}
	}

	void buildLCP()
	{
		for (int i = 0, k = 0; i < N; ++i) if (pos[i] != N - 1)
		{
			for (int j = sa[pos[i] + 1]; S[i + k] == S[j + k];)
			++k;
			lcp[pos[i]] = k;
			if (k)--k;
		}
	}
} // end namespace SuffixArray

namespace HashSuffixArray
{
	const int
		MAXN = 1 << 21;

	typedef unsigned long long hash;

	const hash BASE = 137;

	int N;
	char * S;
	int sa[MAXN];
	hash h[MAXN], hPow[MAXN];

	#define getHash(lo, size) (h[lo] - h[(lo) + (size)] * hPow[size])

	inline bool sufCmp(int i, int j)
	{
		int lo = 1, hi = min(N - i, N - j);
		while (lo <= hi)
		{
			int mid = (lo + hi) >> 1;
			if (getHash(i, mid) == getHash(j, mid))
				lo = mid + 1;
			else
				hi = mid - 1;
		}
		return S[i + hi] < S[j + hi];
	}

	void buildSA()
	{
		N = strlen(S);
		hPow[0] = 1;
		for (int i = 1; i <= N; ++i)
			hPow[i] = hPow[i - 1] * BASE;
		h[N] = 0;
		for (int i = N - 1; i >= 0; --i)
			h[i] = h[i + 1] * BASE + S[i], sa[i] = i;

		stable_sort(sa, sa + N, sufCmp);
	}

} // end namespace HashSuffixArray

namespace lrj_sa
{
	const int MAXN = 1000;
	char s[MAXN];
	int sa[MAXN], t[MAXN], t2[MAXN], c[MAXN], n;	/* n seems to be the length of s. */
	/* every charactor is in [0,m-1] */
	void build_sa(int m)
	{
		int i, *x = t, *y = t2;
		for(i = 0;i < m;++i) c[i] = 0;
		for(i = 0;i < n;i++) c[x[i]=s[i]]++;
		for(i = 1;i < m;++i) c[i] += c[i-1];
		for(i = n-1;i >= 0;--i) sa[--c[x[i]]] = i;
		for(int k = 1;k <= n;k <<= 1)
		{
			int p = 0;
			for(i = n-k;i < n;++i) y[p++] = i;
			for(i = 0;i < n;++i) if(sa[i] >= k) y[p++] = sa[i]-k;
			for(i = 0;i < m;i++) c[i] = 0;
			for(i = 0;i < n;i++) c[x[y[i]]]++;
			for(i = 0;i < m;++i) c[i]+=c[i-1];
			for(i = n-1;i >= 0;--i) sa[--c[x[y[i]]]] = y[i];
			swap(x,y);
			p = 1; x[sa[0]] = 0;
			for(i = 1;i < n;++i)
				x[sa[i]] = y[sa[i-1]]==y[sa[i]] && y[sa[[i-1]+k]]==y[sa[i]+k] ? p-1:p++;
			if(p >= n) break;
			m = p;
		}
	}
	int rank[MAXN], height[MAXN];
	void get_height(void)
	{
		int i,j,k = 0;
		for(i = 0;i < n;++i) rank[sa[i]] = i;
		for(i = 0;i < n;++i)
		{
			if(k) k--;
			j = sa[rank[i]-1];
			while(s[i+k]==s[j+k]) k++;
			height[rank[i]] = k;
		}
	}
} // end namespace lrj_sa
<|endoftext|>


<commit_before>#include "DukeMainWindow.hpp"

#include <duke/engine/overlay/StatisticsOverlay.hpp>
#include <duke/engine/overlay/OnScreenDisplayOverlay.hpp>
#include <duke/engine/overlay/AttributesOverlay.hpp>
#include <duke/engine/ConsoleIO.hpp>
#include <duke/engine/rendering/ImageRenderer.hpp>
#include <duke/engine/commands/Commands.hpp>
#include <duke/time/Clock.hpp>
#include <duke/gl/GL.hpp>

#include <string>
#include <sstream>

namespace duke {

DukeMainWindow::DukeMainWindow(GLFWwindow *pWindow, const CmdLineParameters &parameters) :
		DukeGLFWWindow(pWindow), m_CmdLine(parameters), m_Player(parameters), m_GlyphRenderer(m_GeometryRenderer) {
	m_Context.pGlyphRenderer = &m_GlyphRenderer;
	m_Context.pGeometryRenderer = &m_GeometryRenderer;

	::glfwMakeContextCurrent(m_pWindow);
	::glfwGetWindowSize(m_pWindow, &m_WindowDim.x, &m_WindowDim.y);
	::glfwGetWindowPos(m_pWindow, &m_WindowPos.x, &m_WindowPos.y);
	::glfwSwapInterval(parameters.swapBufferInterval);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable(GL_DEPTH_TEST);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	using std::bind;
	using std::placeholders::_1;
	using std::placeholders::_2;

	mouseButtonCallback = bind(&DukeMainWindow::onMouseClick, this, _1, _2);
	mousePosCallback = bind(&DukeMainWindow::onMouseMove, this, _1, _2);
	scrollCallback = bind(&DukeMainWindow::onScroll, this, _1, _2);
	windowResizeCallback = bind(&DukeMainWindow::onWindowResize, this, _1, _2);
	charCallback = bind(&DukeMainWindow::onChar, this, _1);
	keyCallback = bind(&DukeMainWindow::onKey, this, _1, _2);
	registerCallbacks();

	// registering commands
	using namespace cmd;
	m_Commands.addAndBind<ArgsCmd>( { "args", "display the command's arguments", { cmd::cmd } }, std::cref(m_Commands));
	m_Commands.addAndBind<ManCmd>( { "man", "display the command's help", { cmd::cmd } }, std::cref(m_Commands));
	m_Commands.addAndBind<SuggestCmd>( { "cmds", "suggest a list of commands", { value } }, std::cref(m_Commands));
	m_Commands.addAndBind<SuggestParam>( { "params", "suggest a list of parameters", { value } }, std::cref(m_Parameters));
	m_Commands.addAndBind<FunctionCmd>( { "play", "play the current playlist" }, [&]() {m_Player.setPlaybackSpeed(1);});
	m_Commands.addAndBind<FunctionCmd>( { "stop", "stop playback" }, [&]() {m_Player.setPlaybackSpeed(0);});
	m_Commands.addAndBind<FunctionCmd>( { "begin", "move to the first frame" }, [&]() {m_Player.cue(m_Player.getTimeline().getRange().first);});
	m_Commands.addAndBind<FunctionCmd>( { "end", "move to the last frame" }, [&]() {m_Player.cue(m_Player.getTimeline().getRange().last);});
	m_Commands.addAndBind<FunctionCmd>( { "quit", "quit the application" }, [&]() {glfwSetWindowShouldClose(getHandle(),true);});
//	m_Commands.addAndBind<LsCmd>( { "ls", "list the current directory", { path } });
//	m_Commands.addAndBind<NoOpCmd>( { "next", "move to next clip" });
//	m_Commands.addAndBind<NoOpCmd>( { "previous", "move to previous clip" });
	std::cout << "Type 'cmds' to list available commands" << std::endl;
}

void DukeMainWindow::load(const Timeline& timeline, const FrameDuration& frameDuration, const FitMode fitMode, int speed) {
	m_Player.load(timeline, frameDuration);
	m_Player.setPlaybackSpeed(speed);
	m_Context.fitMode = fitMode;
}

void DukeMainWindow::onKey(int key, int action) {
	if (action == GLFW_PRESS || action == GLFW_REPEAT)
		m_KeyStrokes.push_back(key);
}

void DukeMainWindow::onChar(unsigned int unicodeCodePoint) {
	m_CharStrokes.push_back(unicodeCodePoint);
}

void DukeMainWindow::onWindowResize(int width, int height) {
	::glViewport(0, 0, width, height);
	m_WindowDim.x = width;
	m_WindowDim.y = height;
}

void DukeMainWindow::onMouseMove(int x, int y) {
	int dx = x - m_MousePos.x;
	int dy = y - m_MousePos.y;
	m_MousePos.x = x;
	m_MousePos.y = y;
	if (m_MouseLeftDown)
		onMouseDrag(dx, dy);
}

void DukeMainWindow::onMouseClick(int buttonId, int buttonState) {
	if (buttonId == GLFW_MOUSE_BUTTON_LEFT)
		m_MouseLeftDown = buttonState == GLFW_PRESS;
}

void DukeMainWindow::onScroll(double x, double y) {
	const auto oldZoom = m_Context.zoom;
	auto newZoom = oldZoom;
	newZoom = logf(newZoom);
	newZoom += y / 8;
	newZoom = expf(newZoom);
	m_Context.zoom = newZoom;

	// computing mouse position in image space
	glm::vec2 mousePos = glm::vec2(m_MousePos.x, m_Context.viewport.dimension.y - m_MousePos.y - 1);
	mousePos -= m_Context.viewport.dimension / 2;
	mousePos -= m_Context.pan;
	m_Context.pan -= glm::ivec2(mousePos * ((newZoom / oldZoom) - 1));
}

void DukeMainWindow::onMouseDrag(int dx, int dy) {
	m_Context.pan.x += dx;
	m_Context.pan.y -= dy;
}

namespace { // defining channel mask constants

using glm::bvec4;
const auto r = bvec4(true, false, false, false);
const auto g = bvec4(false, true, false, false);
const auto b = bvec4(false, false, true, false);
const auto a = bvec4(false, false, false, true);
const auto all = bvec4(false);

bool setNextMode(FitMode &mode) {
	switch (mode) {
	case FitMode::FREE:
		mode = FitMode::INNER;
		return true;
	case FitMode::INNER:
		mode = FitMode::OUTER;
		return true;
	case FitMode::OUTER:
		mode = FitMode::ACTUAL;
		return false;
	case FitMode::ACTUAL:
		mode = FitMode::INNER;
		return true;
	}
	throw std::runtime_error("unknown fitmode");
}

const char* getFitModeString(FitMode &mode) {
	switch (mode) {
	case FitMode::ACTUAL:
		return "Actual pixel";
	case FitMode::INNER:
		return "Fit inner frame";
	case FitMode::FREE:
		return "No fit";
	case FitMode::OUTER:
		return "Fit outer frame";
	}
	throw std::runtime_error("unknown fitmode");
}

}  // namespace

void DukeMainWindow::run() {
	ConsoleIO console;
	std::vector<std::string> commands;
	AttributesOverlay metadataOverlay(m_GlyphRenderer);
	OnScreenDisplayOverlay statusOverlay(m_GlyphRenderer);
	StatisticsOverlay statisticOverlay(m_GlyphRenderer, m_Player.getTimeline());
	bool showMetadataOverlay = false;
	bool showStatisticOverlay = true;
	bool doSetupZoom = true;

	SharedMesh pSquare = createSquare();

	size_t lastFrame = 0;
	auto milestone = duke_clock::now();
	bool running = true;

	const auto keyPressed = [=](int key) -> bool {
		return ::glfwGetKey(m_pWindow, key) == GLFW_PRESS;
	};

	const auto shouldClose = [=]() -> bool {
		return ::glfwWindowShouldClose(m_pWindow);
	};

	const auto togglePlayStop = [&]() -> bool {
		const int speed = m_Player.getPlaybackSpeed() == 0 ? 1 : 0;
		m_Player.setPlaybackSpeed(speed);
		return speed != 0;
	};

	const auto display = [&](const std::string &msg) {
		statusOverlay.setString(m_Context.liveTime, msg);
	};

	const auto displayExposure = [&]() {
		std::ostringstream oss;
		oss.precision(3);
		oss << "exposure "<< std::fixed << m_Context.exposure;
		display(oss.str());
	};

	const auto setupZoom = [&]() {
		if(m_Context.fitMode == FitMode::FREE||!doSetupZoom)
		return;
		m_Context.zoom = getZoomValue(m_Context);
		m_Context.pan= glm::ivec2();
		doSetupZoom = false;
	};

	while (running) {
		// fetching user inputs
		::glfwPollEvents();

		// setting up context
		m_Context.viewport = Viewport(glm::ivec2(), m_WindowDim);
		m_Context.currentFrame = m_Player.getCurrentFrame();
		m_Context.playbackTime = m_Player.getPlaybackTime();

		// current frame
		const size_t frame = m_Context.currentFrame.round();

		// preparing current frame textures
		auto &textureCache = m_Player.getTextureCache();
		const auto speed = m_Player.getPlaybackSpeed();
		const auto mode = speed < 0 ? IterationMode::BACKWARD : (speed > 0 ? IterationMode::FORWARD : IterationMode::PINGPONG);
		textureCache.prepare(frame, mode);

		// rendering tracks
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		for (const Track &track : m_Player.getTimeline()) {
			if (track.disabled)
				continue;

			const auto pTrackItr = track.clipContaining(frame);
			if (pTrackItr == track.end())
				continue;

			m_Context.pCurrentImage = nullptr;
			const MediaFrameReference mfr = track.getMediaFrameReferenceAt(frame);
			const auto pMediaStream = mfr.first;
			if (pMediaStream) {
				auto pLoadedTexture = textureCache.getLoadedTexture(mfr);
				if (pLoadedTexture) {
					m_Context.pCurrentImage = pLoadedTexture;
					setupZoom();
					auto &texture = *pLoadedTexture->pTexture;
					auto boundTexture = texture.scope_bind_texture();
					glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
					glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
					renderWithBoundTexture(m_GlyphRenderer.getGeometryRenderer().shaderPool, pSquare.get(), m_Context);
				} else {
					drawText(m_GlyphRenderer, m_Context.viewport, "caching", 100, 100, 1, 3);
				}
			}
			const auto& pOverlayTrack = pTrackItr->second.pOverlay;
			if (pOverlayTrack)
				pOverlayTrack->render(m_Context);
			if (showMetadataOverlay)
				metadataOverlay.render(m_Context);
		}
		if (showStatisticOverlay)
			statisticOverlay.render(m_Context);
		statusOverlay.render(m_Context);

		// displaying
		::glfwSwapBuffers(m_pWindow);

		// updating time
		const auto elapsedMicroSeconds = statisticOverlay.vBlankMetronom.tick();
		const Time offset = m_CmdLine.unlimitedFPS ? m_Player.getFrameDuration() : Time(elapsedMicroSeconds);
		m_Player.offsetPlaybackTime(offset);
		m_Context.liveTime += Time(elapsedMicroSeconds.count(), 1000000);

		if (frame != lastFrame) {
			statisticOverlay.frameMetronom.tick();
			lastFrame = frame;
		}

		// handling input by char
		for (const int key : m_CharStrokes) {
			switch (key) {
			case ' ': {
				const bool playing = togglePlayStop();
				display(playing ? "play" : "stop");
				break;
			}
			case 'r':
				m_Context.channels = m_Context.channels == r ? all : r;
				break;
			case 'g':
				m_Context.channels = m_Context.channels == g ? all : g;
				break;
			case 'b':
				m_Context.channels = m_Context.channels == b ? all : b;
				break;
			case 'a':
				m_Context.channels = m_Context.channels == a ? all : a;
				break;
			case '*':
				m_Context.exposure = 1;
				displayExposure();
				break;
			case '+':
				m_Context.exposure *= 1.2;
				displayExposure();
				break;
			case '-':
				m_Context.exposure /= 1.2;
				displayExposure();
				break;
			case 'm':
				showMetadataOverlay = !showMetadataOverlay;
				break;
			case 's':
				showStatisticOverlay = !showStatisticOverlay;
				break;
			case 'f':
				setNextMode(m_Context.fitMode);
				doSetupZoom = true;
				display(getFitModeString(m_Context.fitMode));
				break;
			}
		}
		m_CharStrokes.clear();

		// handling input by key
		const bool ctrlModifier = keyPressed(GLFW_KEY_LEFT_CONTROL) || keyPressed(GLFW_KEY_RIGHT_CONTROL);
		//		const bool shiftModifier = keyPressed(GLFW_KEY_LEFT_SHIFT) || keyPressed(GLFW_KEY_RIGHT_SHIFT);
		for (const int key : m_KeyStrokes) {
			switch (key) {
			case GLFW_KEY_HOME:
				commands.emplace_back("begin");
				break;
			case GLFW_KEY_END:
				commands.emplace_back("end");
				break;
			case GLFW_KEY_LEFT:
				m_Player.cueRelative(ctrlModifier ? -25 : -1);
				break;
			case GLFW_KEY_RIGHT:
				m_Player.cueRelative(ctrlModifier ? 25 : 1);
				break;
			}
		}
		m_KeyStrokes.clear();

		// checking incoming commands
		console.poll(commands);
		for (const auto& cmd : commands) {
			auto result = m_Commands.execute(cmd);
			if (!result.empty())
				std::cout << result << std::endl;
		}
		commands.clear();

		// check stop
		running = !(shouldClose() || (keyPressed(GLFW_KEY_ESC)));

		// dumping cache state every 200 ms
		const auto now = duke_clock::now();
		if ((now - milestone) > std::chrono::milliseconds(100)) {
			textureCache.getImageCache().dumpState(statisticOverlay.cacheState);
			statisticOverlay.vBlankMetronom.compute();
			statisticOverlay.frameMetronom.compute();
			milestone = now;
		}
	}
}

} /* namespace duke */
<commit_msg>pass the colorspace command line arguments to the context. Also added a test code to render only when the buffer is available, it will surely be removed in the next commits<commit_after>#include "DukeMainWindow.hpp"

#include <duke/engine/overlay/StatisticsOverlay.hpp>
#include <duke/engine/overlay/OnScreenDisplayOverlay.hpp>
#include <duke/engine/overlay/AttributesOverlay.hpp>
#include <duke/engine/ConsoleIO.hpp>
#include <duke/engine/rendering/ImageRenderer.hpp>
#include <duke/engine/commands/Commands.hpp>
#include <duke/time/Clock.hpp>
#include <duke/gl/GL.hpp>

#include <string>
#include <sstream>

namespace duke {

DukeMainWindow::DukeMainWindow(GLFWwindow *pWindow, const CmdLineParameters &parameters) :
		DukeGLFWWindow(pWindow), m_CmdLine(parameters), m_Player(parameters), m_GlyphRenderer(m_GeometryRenderer) {
	m_Context.pGlyphRenderer = &m_GlyphRenderer;
	m_Context.pGeometryRenderer = &m_GeometryRenderer;
	m_Context.fileColorSpace = parameters.inputColorSpace;
	m_Context.screenColorSpace = parameters.outputColorSpace;

	::glfwMakeContextCurrent(m_pWindow);
	::glfwGetWindowSize(m_pWindow, &m_WindowDim.x, &m_WindowDim.y);
	::glfwGetWindowPos(m_pWindow, &m_WindowPos.x, &m_WindowPos.y);
	::glfwSwapInterval(parameters.swapBufferInterval);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable(GL_DEPTH_TEST);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	using std::bind;
	using std::placeholders::_1;
	using std::placeholders::_2;

	mouseButtonCallback = bind(&DukeMainWindow::onMouseClick, this, _1, _2);
	mousePosCallback = bind(&DukeMainWindow::onMouseMove, this, _1, _2);
	scrollCallback = bind(&DukeMainWindow::onScroll, this, _1, _2);
	windowResizeCallback = bind(&DukeMainWindow::onWindowResize, this, _1, _2);
	charCallback = bind(&DukeMainWindow::onChar, this, _1);
	keyCallback = bind(&DukeMainWindow::onKey, this, _1, _2);
	registerCallbacks();

	// registering commands
	using namespace cmd;
	m_Commands.addAndBind<ArgsCmd>( { "args", "display the command's arguments", { cmd::cmd } }, std::cref(m_Commands));
	m_Commands.addAndBind<ManCmd>( { "man", "display the command's help", { cmd::cmd } }, std::cref(m_Commands));
	m_Commands.addAndBind<SuggestCmd>( { "cmds", "suggest a list of commands", { value } }, std::cref(m_Commands));
	m_Commands.addAndBind<SuggestParam>( { "params", "suggest a list of parameters", { value } }, std::cref(m_Parameters));
	m_Commands.addAndBind<FunctionCmd>( { "play", "play the current playlist" }, [&]() {m_Player.setPlaybackSpeed(1);});
	m_Commands.addAndBind<FunctionCmd>( { "stop", "stop playback" }, [&]() {m_Player.setPlaybackSpeed(0);});
	m_Commands.addAndBind<FunctionCmd>( { "begin", "move to the first frame" }, [&]() {m_Player.cue(m_Player.getTimeline().getRange().first);});
	m_Commands.addAndBind<FunctionCmd>( { "end", "move to the last frame" }, [&]() {m_Player.cue(m_Player.getTimeline().getRange().last);});
	m_Commands.addAndBind<FunctionCmd>( { "quit", "quit the application" }, [&]() {glfwSetWindowShouldClose(getHandle(),true);});
//	m_Commands.addAndBind<LsCmd>( { "ls", "list the current directory", { path } });
//	m_Commands.addAndBind<NoOpCmd>( { "next", "move to next clip" });
//	m_Commands.addAndBind<NoOpCmd>( { "previous", "move to previous clip" });
	std::cout << "Type 'cmds' to list available commands" << std::endl;
}

void DukeMainWindow::load(const Timeline& timeline, const FrameDuration& frameDuration, const FitMode fitMode, int speed) {
	m_Player.load(timeline, frameDuration);
	m_Player.setPlaybackSpeed(speed);
	m_Context.fitMode = fitMode;
}

void DukeMainWindow::onKey(int key, int action) {
	if (action == GLFW_PRESS || action == GLFW_REPEAT)
		m_KeyStrokes.push_back(key);
}

void DukeMainWindow::onChar(unsigned int unicodeCodePoint) {
	m_CharStrokes.push_back(unicodeCodePoint);
}

void DukeMainWindow::onWindowResize(int width, int height) {
	::glViewport(0, 0, width, height);
	m_WindowDim.x = width;
	m_WindowDim.y = height;
}

void DukeMainWindow::onMouseMove(int x, int y) {
	int dx = x - m_MousePos.x;
	int dy = y - m_MousePos.y;
	m_MousePos.x = x;
	m_MousePos.y = y;
	if (m_MouseLeftDown)
		onMouseDrag(dx, dy);
}

void DukeMainWindow::onMouseClick(int buttonId, int buttonState) {
	if (buttonId == GLFW_MOUSE_BUTTON_LEFT)
		m_MouseLeftDown = buttonState == GLFW_PRESS;
}

void DukeMainWindow::onScroll(double x, double y) {
	const auto oldZoom = m_Context.zoom;
	auto newZoom = oldZoom;
	newZoom = logf(newZoom);
	newZoom += y / 8;
	newZoom = expf(newZoom);
	m_Context.zoom = newZoom;

	// computing mouse position in image space
	glm::vec2 mousePos = glm::vec2(m_MousePos.x, m_Context.viewport.dimension.y - m_MousePos.y - 1);
	mousePos -= m_Context.viewport.dimension / 2;
	mousePos -= m_Context.pan;
	m_Context.pan -= glm::ivec2(mousePos * ((newZoom / oldZoom) - 1));
}

void DukeMainWindow::onMouseDrag(int dx, int dy) {
	m_Context.pan.x += dx;
	m_Context.pan.y -= dy;
}

namespace { // defining channel mask constants

using glm::bvec4;
const auto r = bvec4(true, false, false, false);
const auto g = bvec4(false, true, false, false);
const auto b = bvec4(false, false, true, false);
const auto a = bvec4(false, false, false, true);
const auto all = bvec4(false);

bool setNextMode(FitMode &mode) {
	switch (mode) {
	case FitMode::FREE:
		mode = FitMode::INNER;
		return true;
	case FitMode::INNER:
		mode = FitMode::OUTER;
		return true;
	case FitMode::OUTER:
		mode = FitMode::ACTUAL;
		return false;
	case FitMode::ACTUAL:
		mode = FitMode::INNER;
		return true;
	}
	throw std::runtime_error("unknown fitmode");
}

const char* getFitModeString(FitMode &mode) {
	switch (mode) {
	case FitMode::ACTUAL:
		return "Actual pixel";
	case FitMode::INNER:
		return "Fit inner frame";
	case FitMode::FREE:
		return "No fit";
	case FitMode::OUTER:
		return "Fit outer frame";
	}
	throw std::runtime_error("unknown fitmode");
}

}  // namespace

void DukeMainWindow::run() {
	ConsoleIO console;
	std::vector<std::string> commands;
	AttributesOverlay metadataOverlay(m_GlyphRenderer);
	OnScreenDisplayOverlay statusOverlay(m_GlyphRenderer);
	StatisticsOverlay statisticOverlay(m_GlyphRenderer, m_Player.getTimeline());
	bool showMetadataOverlay = false;
	bool showStatisticOverlay = true;
	bool doSetupZoom = true;

	SharedMesh pSquare = createSquare();

	size_t lastFrame = 0;
	auto milestone = duke_clock::now();
	bool running = true;

	const auto keyPressed = [=](int key) -> bool {
		return ::glfwGetKey(m_pWindow, key) == GLFW_PRESS;
	};

	const auto shouldClose = [=]() -> bool {
		return ::glfwWindowShouldClose(m_pWindow);
	};

	const auto togglePlayStop = [&]() -> bool {
		const int speed = m_Player.getPlaybackSpeed() == 0 ? 1 : 0;
		m_Player.setPlaybackSpeed(speed);
		return speed != 0;
	};

	const auto display = [&](const std::string &msg) {
		statusOverlay.setString(m_Context.liveTime, msg);
	};

	const auto displayExposure = [&]() {
		std::ostringstream oss;
		oss.precision(3);
		oss << "exposure "<< std::fixed << m_Context.exposure;
		display(oss.str());
	};

	const auto setupZoom = [&]() {
		if(m_Context.fitMode == FitMode::FREE||!doSetupZoom)
		return;
		m_Context.zoom = getZoomValue(m_Context);
		m_Context.pan= glm::ivec2();
		doSetupZoom = false;
	};

	while (running) {
		// fetching user inputs
		::glfwPollEvents();

		// setting up context
		m_Context.viewport = Viewport(glm::ivec2(), m_WindowDim);
		m_Context.currentFrame = m_Player.getCurrentFrame();
		m_Context.playbackTime = m_Player.getPlaybackTime();

		// current frame
		const size_t frame = m_Context.currentFrame.round();

		// preparing current frame textures
		auto &textureCache = m_Player.getTextureCache();
		const auto speed = m_Player.getPlaybackSpeed();
		const auto mode = speed < 0 ? IterationMode::BACKWARD : (speed > 0 ? IterationMode::FORWARD : IterationMode::PINGPONG);
		textureCache.prepare(frame, mode);

		// rendering tracks
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		for (const Track &track : m_Player.getTimeline()) {
			if (track.disabled)
				continue;

			const auto pTrackItr = track.clipContaining(frame);
			if (pTrackItr == track.end())
				continue;

			m_Context.pCurrentImage = nullptr;
			const MediaFrameReference mfr = track.getMediaFrameReferenceAt(frame);
			const auto pMediaStream = mfr.first;

			if (pMediaStream) {
				auto pLoadedTexture = textureCache.getLoadedTexture(mfr);
				// Mode where we force the texture to be ready before displaying something
				if (m_CmdLine.unlimitedFPS) {
					int MaxTry = 1000;
					while (!pLoadedTexture) {
						// The texture was not ready, re-ask for it
						textureCache.prepare(frame, mode);
						pLoadedTexture = textureCache.getLoadedTexture(mfr);
						if (MaxTry--<=0)
							continue;
					}
				}
				if (pLoadedTexture) {
					m_Context.pCurrentImage = pLoadedTexture;
					setupZoom();
					auto &texture = *pLoadedTexture->pTexture;
					auto boundTexture = texture.scope_bind_texture();
					glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
					glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
					renderWithBoundTexture(m_GlyphRenderer.getGeometryRenderer().shaderPool, pSquare.get(), m_Context);
				} else {
					drawText(m_GlyphRenderer, m_Context.viewport, "caching", 100, 100, 1, 3);
				}
			}
			const auto& pOverlayTrack = pTrackItr->second.pOverlay;
			if (pOverlayTrack)
				pOverlayTrack->render(m_Context);
			if (showMetadataOverlay)
				metadataOverlay.render(m_Context);
		}
		if (showStatisticOverlay)
			statisticOverlay.render(m_Context);
		statusOverlay.render(m_Context);

		// displaying
		::glfwSwapBuffers(m_pWindow);

		// updating time
		const auto elapsedMicroSeconds = statisticOverlay.vBlankMetronom.tick();
		const Time offset = m_CmdLine.unlimitedFPS ? m_Player.getFrameDuration() : Time(elapsedMicroSeconds);
		m_Player.offsetPlaybackTime(offset);

		m_Context.liveTime += Time(elapsedMicroSeconds.count(), 1000000);

		if (frame != lastFrame) {
			statisticOverlay.frameMetronom.tick();
			lastFrame = frame;
		}

		// handling input by char
		for (const int key : m_CharStrokes) {
			switch (key) {
			case ' ': {
				const bool playing = togglePlayStop();
				display(playing ? "play" : "stop");
				break;
			}
			case 'r':
				m_Context.channels = m_Context.channels == r ? all : r;
				break;
			case 'g':
				m_Context.channels = m_Context.channels == g ? all : g;
				break;
			case 'b':
				m_Context.channels = m_Context.channels == b ? all : b;
				break;
			case 'a':
				m_Context.channels = m_Context.channels == a ? all : a;
				break;
			case '*':
				m_Context.exposure = 1;
				displayExposure();
				break;
			case '+':
				m_Context.exposure *= 1.2;
				displayExposure();
				break;
			case '-':
				m_Context.exposure /= 1.2;
				displayExposure();
				break;
			case 'm':
				showMetadataOverlay = !showMetadataOverlay;
				break;
			case 's':
				showStatisticOverlay = !showStatisticOverlay;
				break;
			case 'f':
				setNextMode(m_Context.fitMode);
				doSetupZoom = true;
				display(getFitModeString(m_Context.fitMode));
				break;
			}
		}
		m_CharStrokes.clear();

		// handling input by key
		const bool ctrlModifier = keyPressed(GLFW_KEY_LEFT_CONTROL) || keyPressed(GLFW_KEY_RIGHT_CONTROL);
		//		const bool shiftModifier = keyPressed(GLFW_KEY_LEFT_SHIFT) || keyPressed(GLFW_KEY_RIGHT_SHIFT);
		for (const int key : m_KeyStrokes) {
			switch (key) {
			case GLFW_KEY_HOME:
				commands.emplace_back("begin");
				break;
			case GLFW_KEY_END:
				commands.emplace_back("end");
				break;
			case GLFW_KEY_LEFT:
				m_Player.cueRelative(ctrlModifier ? -25 : -1);
				break;
			case GLFW_KEY_RIGHT:
				m_Player.cueRelative(ctrlModifier ? 25 : 1);
				break;
			}
		}
		m_KeyStrokes.clear();

		// checking incoming commands
		console.poll(commands);
		for (const auto& cmd : commands) {
			auto result = m_Commands.execute(cmd);
			if (!result.empty())
				std::cout << result << std::endl;
		}
		commands.clear();

		// check stop
		running = !(shouldClose() || (keyPressed(GLFW_KEY_ESC)));

		// dumping cache state every 200 ms
		const auto now = duke_clock::now();
		if ((now - milestone) > std::chrono::milliseconds(100)) {
			textureCache.getImageCache().dumpState(statisticOverlay.cacheState);
			statisticOverlay.vBlankMetronom.compute();
			statisticOverlay.frameMetronom.compute();
			milestone = now;
		}
	}
}

} /* namespace duke */
<|endoftext|>


<commit_before>#ifndef MAPNIK_BENCH_FRAMEWORK_HPP
#define MAPNIK_BENCH_FRAMEWORK_HPP

// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/params.hpp>
#include <mapnik/value/types.hpp>
#include <mapnik/safe_cast.hpp>
#include "../test/cleanup.hpp"

// stl
#include <chrono>
#include <cmath> // log10, round
#include <cstdio> // snprintf
#include <iostream>
#include <set>
#include <sstream>
#include <thread>
#include <vector>

namespace benchmark {

template <typename T>
using milliseconds = std::chrono::duration<T, std::milli>;

template <typename T>
using seconds = std::chrono::duration<T>;

class test_case
{
protected:
    mapnik::parameters params_;
    std::size_t threads_;
    std::size_t iterations_;
public:
    test_case(mapnik::parameters const& params)
       : params_(params),
         threads_(mapnik::safe_cast<std::size_t>(*params.get<mapnik::value_integer>("threads", 0))),
         iterations_(mapnik::safe_cast<std::size_t>(*params.get<mapnik::value_integer>("iterations", 0)))
         {}
    std::size_t threads() const
    {
        return threads_;
    }
    std::size_t iterations() const
    {
        return iterations_;
    }
    mapnik::parameters const& params() const
    {
        return params_;
    }
    virtual bool validate() const = 0;
    virtual bool operator()() const = 0;
};

// gathers --long-option values in 'params';
// returns the index of the first non-option argument,
// or negated index of an ill-formed option argument
inline int parse_args(int argc, char** argv, mapnik::parameters & params)
{
    for (int i = 1; i < argc; ++i) {
        const char* opt = argv[i];
        if (opt[0] != '-') {
            // non-option argument, return its index
            return i;
        }
        if (opt[1] != '-') {
            // we only accept --long-options, but instead of throwing,
            // just issue a warning and let the caller decide what to do
            std::clog << argv[0] << ": invalid option '" << opt << "'\n";
            return -i; // negative means ill-formed option #i
        }
        if (opt[2] == '\0') {
            // option-list terminator '--'
            return i + 1;
        }

        // take option name without the leading '--'
        std::string key(opt + 2);
        size_t eq = key.find('=');
        if (eq != std::string::npos) {
            // one-argument form '--foo=bar'
            params[key.substr(0, eq)] = key.substr(eq + 1);
        }
        else if (i + 1 < argc) {
            // two-argument form '--foo' 'bar'
            params[key] = std::string(argv[++i]);
        }
        else {
            // missing second argument
            std::clog << argv[0] << ": missing option '" << opt << "' value\n";
            return -i; // negative means ill-formed option #i
        }
    }
    return argc; // there were no non-option arguments
}

inline void handle_common_args(mapnik::parameters const& params)
{
    if (auto severity = params.get<std::string>("log")) {
        if (*severity == "debug")
            mapnik::logger::set_severity(mapnik::logger::debug);
        else if (*severity == "warn")
            mapnik::logger::set_severity(mapnik::logger::warn);
        else if (*severity == "error")
            mapnik::logger::set_severity(mapnik::logger::error);
        else if (*severity == "none")
            mapnik::logger::set_severity(mapnik::logger::none);
        else
            std::clog << "ignoring option --log='" << *severity
                      << "' (allowed values are: debug, warn, error, none)\n";
    }
}

inline int handle_args(int argc, char** argv, mapnik::parameters & params)
{
    int res = parse_args(argc, argv, params);
    handle_common_args(params);
    return res;
}

#define BENCHMARK(test_class,name)                      \
    int main(int argc, char** argv)                     \
    {                                                   \
        try                                             \
        {                                               \
            mapnik::parameters params;                  \
            benchmark::handle_args(argc,argv,params);   \
            test_class test_runner(params);             \
            auto result = run(test_runner,name);        \
            testing::run_cleanup();                     \
            return result;                              \
        }                                               \
        catch (std::exception const& ex)                \
        {                                               \
            std::clog << ex.what() << "\n";             \
            testing::run_cleanup();                     \
            return -1;                                  \
        }                                               \
    }                                                   \

struct big_number_fmt
{
    int w;
    double v;
    const char* u;

    big_number_fmt(int width, double value, int base = 1000)
        : w(width), v(value), u("")
    {
        static const char* suffixes = "\0\0k\0M\0G\0T\0P\0E\0Z\0Y\0\0";
        u = suffixes;

        while (v > 1 && std::log10(std::round(v)) >= width && u[2])
        {
            v /= base;
            u += 2;
        }

        // adjust width for proper alignment without suffix
        w += (u == suffixes);
    }
};

template <typename T>
int run(T const& test_runner, std::string const& name)
{
    try
    {
        if (!test_runner.validate())
        {
            std::clog << "test did not validate: " << name << "\n";
            return 1;
        }
        // run test once before timing
        // if it returns false then we'll abort timing
        if (!test_runner())
        {
            return 2;
        }

        std::chrono::high_resolution_clock::time_point start;
        std::chrono::high_resolution_clock::duration elapsed;
        auto opt_min_duration = test_runner.params().template get<double>("min-duration", 0.0);
        std::chrono::duration<double> min_seconds(*opt_min_duration);
        auto min_duration = std::chrono::duration_cast<decltype(elapsed)>(min_seconds);
        auto num_iters = test_runner.iterations();
        auto num_threads = test_runner.threads();
        auto total_iters = 0;

        if (num_threads > 0)
        {
            std::mutex mtx_ready;
            std::unique_lock<std::mutex> lock_ready(mtx_ready);

            auto stub = [&](T const& test_copy)
            {
                // workers will wait on this mutex until the main thread
                // constructs all of them and starts measuring time
                std::unique_lock<std::mutex> my_lock(mtx_ready);
                my_lock.unlock();
                test_copy();
            };

            std::vector<std::thread> tg;
            tg.reserve(num_threads);
            for (auto i = num_threads; i-- > 0; )
            {
                tg.emplace_back(stub, test_runner);
            }
            start = std::chrono::high_resolution_clock::now();
            lock_ready.unlock();
            // wait for all workers to finish
            for (auto & t : tg)
            {
                if (t.joinable())
                    t.join();
            }
            elapsed = std::chrono::high_resolution_clock::now() - start;
            // this is actually per-thread count, not total, but I think
            // reporting average 'iters/thread/second' is more useful
            // than 'iters/second' multiplied by the number of threads
            total_iters += num_iters;
        }
        else
        {
            start = std::chrono::high_resolution_clock::now();
            do {
                test_runner();
                elapsed = std::chrono::high_resolution_clock::now() - start;
                total_iters += num_iters;
            } while (elapsed < min_duration);
        }

        char msg[200];
        double dur_total = milliseconds<double>(elapsed).count();
        auto elapsed_nonzero = std::max(elapsed, decltype(elapsed){1});
        big_number_fmt itersf(4, total_iters);
        big_number_fmt ips(5, total_iters / seconds<double>(elapsed_nonzero).count());

        std::snprintf(msg, sizeof(msg),
                "%-43s %3zu thread(s) %*.0f%s iters %6.0f milliseconds %*.0f%s i/s\n",
                name.c_str(),
                num_threads,
                itersf.w, itersf.v, itersf.u,
                dur_total,
                ips.w, ips.v, ips.u
                );
        std::clog << msg;
        return 0;
    }
    catch (std::exception const& ex)
    {
        std::clog << "test runner did not complete: " << ex.what() << "\n";
        return 4;
    }
}

struct sequencer
{
    sequencer(int argc, char** argv)
      : exit_code_(0)
    {
        benchmark::handle_args(argc, argv, params_);
    }

    int done() const
    {
        return exit_code_;
    }

    template <typename Test, typename... Args>
    sequencer & run(std::string const& name, Args && ...args)
    {
        // Test instance lifetime is confined to this function
        Test test_runner(params_, std::forward<Args>(args)...);
        // any failing test run will make exit code non-zero
        exit_code_ |= benchmark::run(test_runner, name);
        return *this; // allow chaining calls
    }

protected:
    mapnik::parameters params_;
    int exit_code_;
};

}

#endif // MAPNIK_BENCH_FRAMEWORK_HPP
<commit_msg>benchmarks: clarify output<commit_after>#ifndef MAPNIK_BENCH_FRAMEWORK_HPP
#define MAPNIK_BENCH_FRAMEWORK_HPP

// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/params.hpp>
#include <mapnik/value/types.hpp>
#include <mapnik/safe_cast.hpp>
#include "../test/cleanup.hpp"

// stl
#include <chrono>
#include <cmath> // log10, round
#include <cstdio> // snprintf
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <thread>
#include <vector>

namespace benchmark {

template <typename T>
using milliseconds = std::chrono::duration<T, std::milli>;

template <typename T>
using seconds = std::chrono::duration<T>;

class test_case
{
protected:
    mapnik::parameters params_;
    std::size_t threads_;
    std::size_t iterations_;
public:
    test_case(mapnik::parameters const& params)
       : params_(params),
         threads_(mapnik::safe_cast<std::size_t>(*params.get<mapnik::value_integer>("threads", 0))),
         iterations_(mapnik::safe_cast<std::size_t>(*params.get<mapnik::value_integer>("iterations", 0)))
         {}
    std::size_t threads() const
    {
        return threads_;
    }
    std::size_t iterations() const
    {
        return iterations_;
    }
    mapnik::parameters const& params() const
    {
        return params_;
    }
    virtual bool validate() const = 0;
    virtual bool operator()() const = 0;
};

// gathers --long-option values in 'params';
// returns the index of the first non-option argument,
// or negated index of an ill-formed option argument
inline int parse_args(int argc, char** argv, mapnik::parameters & params)
{
    for (int i = 1; i < argc; ++i) {
        const char* opt = argv[i];
        if (opt[0] != '-') {
            // non-option argument, return its index
            return i;
        }
        if (opt[1] != '-') {
            // we only accept --long-options, but instead of throwing,
            // just issue a warning and let the caller decide what to do
            std::clog << argv[0] << ": invalid option '" << opt << "'\n";
            return -i; // negative means ill-formed option #i
        }
        if (opt[2] == '\0') {
            // option-list terminator '--'
            return i + 1;
        }

        // take option name without the leading '--'
        std::string key(opt + 2);
        size_t eq = key.find('=');
        if (eq != std::string::npos) {
            // one-argument form '--foo=bar'
            params[key.substr(0, eq)] = key.substr(eq + 1);
        }
        else if (i + 1 < argc) {
            // two-argument form '--foo' 'bar'
            params[key] = std::string(argv[++i]);
        }
        else {
            // missing second argument
            std::clog << argv[0] << ": missing option '" << opt << "' value\n";
            return -i; // negative means ill-formed option #i
        }
    }
    return argc; // there were no non-option arguments
}

inline void handle_common_args(mapnik::parameters const& params)
{
    if (auto severity = params.get<std::string>("log")) {
        if (*severity == "debug")
            mapnik::logger::set_severity(mapnik::logger::debug);
        else if (*severity == "warn")
            mapnik::logger::set_severity(mapnik::logger::warn);
        else if (*severity == "error")
            mapnik::logger::set_severity(mapnik::logger::error);
        else if (*severity == "none")
            mapnik::logger::set_severity(mapnik::logger::none);
        else
            std::clog << "ignoring option --log='" << *severity
                      << "' (allowed values are: debug, warn, error, none)\n";
    }
}

inline int handle_args(int argc, char** argv, mapnik::parameters & params)
{
    int res = parse_args(argc, argv, params);
    handle_common_args(params);
    return res;
}

#define BENCHMARK(test_class,name)                      \
    int main(int argc, char** argv)                     \
    {                                                   \
        try                                             \
        {                                               \
            mapnik::parameters params;                  \
            benchmark::handle_args(argc,argv,params);   \
            test_class test_runner(params);             \
            auto result = run(test_runner,name);        \
            testing::run_cleanup();                     \
            return result;                              \
        }                                               \
        catch (std::exception const& ex)                \
        {                                               \
            std::clog << ex.what() << "\n";             \
            testing::run_cleanup();                     \
            return -1;                                  \
        }                                               \
    }                                                   \

struct big_number_fmt
{
    int w;
    double v;
    const char* u;

    big_number_fmt(int width, double value, int base = 1000)
        : w(width), v(value), u("")
    {
        static const char* suffixes = "\0\0k\0M\0G\0T\0P\0E\0Z\0Y\0\0";
        u = suffixes;

        while (v > 1 && std::log10(std::round(v)) >= width && u[2])
        {
            v /= base;
            u += 2;
        }

        // adjust width for proper alignment without suffix
        w += (u == suffixes);
    }
};

template <typename T>
int run(T const& test_runner, std::string const& name)
{
    try
    {
        if (!test_runner.validate())
        {
            std::clog << "test did not validate: " << name << "\n";
            return 1;
        }
        // run test once before timing
        // if it returns false then we'll abort timing
        if (!test_runner())
        {
            return 2;
        }

        std::chrono::high_resolution_clock::time_point start;
        std::chrono::high_resolution_clock::duration elapsed;
        auto opt_min_duration = test_runner.params().template get<double>("min-duration", 0.0);
        std::chrono::duration<double> min_seconds(*opt_min_duration);
        auto min_duration = std::chrono::duration_cast<decltype(elapsed)>(min_seconds);
        auto num_iters = test_runner.iterations();
        auto num_threads = test_runner.threads();
        auto total_iters = 0;

        if (num_threads > 0)
        {
            std::mutex mtx_ready;
            std::unique_lock<std::mutex> lock_ready(mtx_ready);

            auto stub = [&](T const& test_copy)
            {
                // workers will wait on this mutex until the main thread
                // constructs all of them and starts measuring time
                std::unique_lock<std::mutex> my_lock(mtx_ready);
                my_lock.unlock();
                test_copy();
            };

            std::vector<std::thread> tg;
            tg.reserve(num_threads);
            for (auto i = num_threads; i-- > 0; )
            {
                tg.emplace_back(stub, test_runner);
            }
            start = std::chrono::high_resolution_clock::now();
            lock_ready.unlock();
            // wait for all workers to finish
            for (auto & t : tg)
            {
                if (t.joinable())
                    t.join();
            }
            elapsed = std::chrono::high_resolution_clock::now() - start;
            // this is actually per-thread count, not total, but I think
            // reporting average 'iters/thread/second' is more useful
            // than 'iters/second' multiplied by the number of threads
            total_iters += num_iters;
        }
        else
        {
            start = std::chrono::high_resolution_clock::now();
            do {
                test_runner();
                elapsed = std::chrono::high_resolution_clock::now() - start;
                total_iters += num_iters;
            } while (elapsed < min_duration);
        }

        char msg[200];
        double dur_total = milliseconds<double>(elapsed).count();
        auto elapsed_nonzero = std::max(elapsed, decltype(elapsed){1});
        big_number_fmt itersf(4, total_iters);
        big_number_fmt ips(5, total_iters / seconds<double>(elapsed_nonzero).count());

        std::clog << std::left << std::setw(43) << name;
        std::clog << std::resetiosflags(std::ios::adjustfield);
        if (num_threads > 0) {
            std::clog << ' ' << std::setw(3) << num_threads
                << " worker" << (num_threads > 1 ? "s" : " ");
        }
        else {
            std::clog << " main thread";
        }
        std::snprintf(msg, sizeof(msg),
                " %*.0f%s iters %6.0f milliseconds %*.0f%s i/t/s\n",
                itersf.w, itersf.v, itersf.u, dur_total,
                ips.w, ips.v, ips.u);
        std::clog << msg;
        return 0;
    }
    catch (std::exception const& ex)
    {
        std::clog << "test runner did not complete: " << ex.what() << "\n";
        return 4;
    }
}

struct sequencer
{
    sequencer(int argc, char** argv)
      : exit_code_(0)
    {
        benchmark::handle_args(argc, argv, params_);
    }

    int done() const
    {
        return exit_code_;
    }

    template <typename Test, typename... Args>
    sequencer & run(std::string const& name, Args && ...args)
    {
        // Test instance lifetime is confined to this function
        Test test_runner(params_, std::forward<Args>(args)...);
        // any failing test run will make exit code non-zero
        exit_code_ |= benchmark::run(test_runner, name);
        return *this; // allow chaining calls
    }

protected:
    mapnik::parameters params_;
    int exit_code_;
};

}

#endif // MAPNIK_BENCH_FRAMEWORK_HPP
<|endoftext|>


<commit_before>#include "joedb/journal/Stream_File.h"
#include "joedb/Exception.h"

namespace joedb
{
 /////////////////////////////////////////////////////////////////////////////
 Stream_File::Stream_File(std::streambuf &streambuf, Open_Mode mode):
 /////////////////////////////////////////////////////////////////////////////
  Generic_File(mode),
  streambuf(streambuf)
 {
 }

 /////////////////////////////////////////////////////////////////////////////
 int64_t Stream_File::raw_get_size() const
 /////////////////////////////////////////////////////////////////////////////
 {
  const auto pos = streambuf.pubseekoff
  (
   0,
   std::ios_base::cur,
   std::ios_base::in
  );

  const auto result = streambuf.pubseekoff
  (
   0,
   std::ios_base::end,
   std::ios_base::in
  );

  streambuf.pubseekoff
  (
   pos,
   std::ios_base::beg,
   std::ios_base::in
  );

  return int64_t(result);
 }

 /////////////////////////////////////////////////////////////////////////////
 size_t Stream_File::raw_read(char *buffer, size_t size)
 /////////////////////////////////////////////////////////////////////////////
 {
  return size_t(streambuf.sgetn(buffer, std::streamsize(size)));
 }

 /////////////////////////////////////////////////////////////////////////////
 void Stream_File::raw_write(const char *buffer, size_t size)
 /////////////////////////////////////////////////////////////////////////////
 {
  size_t written = 0;

  while (written < size)
  {
   const auto result = streambuf.sputn
   (
    buffer + written,
    std::streamsize(size - written)
   );
   if (result <= 0)
    throw Exception("Could not write to stream");
   written += size_t(result);
  }
 }

 /////////////////////////////////////////////////////////////////////////////
 int Stream_File::raw_seek(int64_t offset)
 /////////////////////////////////////////////////////////////////////////////
 {
  if (offset < 0)
   return 1;

  const auto pos = streambuf.pubseekoff(offset, std::ios_base::beg);
  return pos != offset;
 }
}
<commit_msg>add missing streambuf.pubsync()<commit_after>#include "joedb/journal/Stream_File.h"
#include "joedb/Exception.h"

namespace joedb
{
 /////////////////////////////////////////////////////////////////////////////
 Stream_File::Stream_File(std::streambuf &streambuf, Open_Mode mode):
 /////////////////////////////////////////////////////////////////////////////
  Generic_File(mode),
  streambuf(streambuf)
 {
 }

 /////////////////////////////////////////////////////////////////////////////
 int64_t Stream_File::raw_get_size() const
 /////////////////////////////////////////////////////////////////////////////
 {
  const auto pos = streambuf.pubseekoff
  (
   0,
   std::ios_base::cur,
   std::ios_base::in
  );

  const auto result = streambuf.pubseekoff
  (
   0,
   std::ios_base::end,
   std::ios_base::in
  );

  streambuf.pubseekoff
  (
   pos,
   std::ios_base::beg,
   std::ios_base::in
  );

  return int64_t(result);
 }

 /////////////////////////////////////////////////////////////////////////////
 size_t Stream_File::raw_read(char *buffer, size_t size)
 /////////////////////////////////////////////////////////////////////////////
 {
  return size_t(streambuf.sgetn(buffer, std::streamsize(size)));
 }

 /////////////////////////////////////////////////////////////////////////////
 void Stream_File::raw_write(const char *buffer, size_t size)
 /////////////////////////////////////////////////////////////////////////////
 {
  size_t written = 0;

  while (written < size)
  {
   const auto result = streambuf.sputn
   (
    buffer + written,
    std::streamsize(size - written)
   );
   if (result <= 0)
    throw Exception("Could not write to stream");
   written += size_t(result);
  }

  streambuf.pubsync();
 }

 /////////////////////////////////////////////////////////////////////////////
 int Stream_File::raw_seek(int64_t offset)
 /////////////////////////////////////////////////////////////////////////////
 {
  if (offset < 0)
   return 1;

  const auto pos = streambuf.pubseekoff(offset, std::ios_base::beg);
  return pos != offset;
 }
}
<|endoftext|>


<commit_before>#include "SkTestImageFilters.h"
#include "SkCanvas.h"
#include "SkDevice.h"

class OwnDeviceCanvas : public SkCanvas {
public:
    OwnDeviceCanvas(SkDevice* device) : SkCanvas(device) {
        SkSafeUnref(device);
    }
};

bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                        const SkMatrix& matrix,
                                        SkBitmap* result,
                                        SkIPoint* loc) {
    SkVector vec;
    matrix.mapVectors(&vec, &fOffset, 1);

    loc->fX += SkScalarRoundToInt(vec.fX);
    loc->fY += SkScalarRoundToInt(vec.fY);
    *result = src;
    return true;
}

bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
                                         SkIRect* dst) {
    SkVector vec;
    ctm.mapVectors(&vec, &fOffset, 1);

    *dst = src;
    dst->offset(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY));
    return true;
}

void SkOffsetImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);
    buffer.writeScalar(fOffset.x());
    buffer.writeScalar(fOffset.y());
}

SkOffsetImageFilter::SkOffsetImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    fOffset.fX = buffer.readScalar();
    fOffset.fY = buffer.readScalar();
}

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

SkComposeImageFilter::~SkComposeImageFilter() {
    SkSafeUnref(fInner);
    SkSafeUnref(fOuter);
}

bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
                                         const SkBitmap& src,
                                         const SkMatrix& ctm,
                                         SkBitmap* result,
                                         SkIPoint* loc) {
    if (!fOuter && !fInner) {
        return false;
    }

    if (!fOuter || !fInner) {
        return (fOuter ? fOuter : fInner)->filterImage(proxy, src, ctm, result, loc);
    }

    SkBitmap tmp;
    return fInner->filterImage(proxy, src, ctm, &tmp, loc) &&
           fOuter->filterImage(proxy, tmp, ctm, result, loc);
}

bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
                                          const SkMatrix& ctm,
                                          SkIRect* dst) {
    if (!fOuter && !fInner) {
        return false;
    }

    if (!fOuter || !fInner) {
        return (fOuter ? fOuter : fInner)->filterBounds(src, ctm, dst);
    }

    SkIRect tmp;
    return fInner->filterBounds(src, ctm, &tmp) &&
           fOuter->filterBounds(tmp, ctm, dst);
}

void SkComposeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

    buffer.writeFlattenable(fOuter);
    buffer.writeFlattenable(fInner);
}

SkComposeImageFilter::SkComposeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    fOuter = (SkImageFilter*)buffer.readFlattenable();
    fInner = (SkImageFilter*)buffer.readFlattenable();
}

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

template <typename T> T* SkSafeRefReturn(T* obj) {
    SkSafeRef(obj);
    return obj;
}

void SkMergeImageFilter::initAlloc(int count, bool hasModes) {
    if (count < 1) {
        fFilters = NULL;
        fModes = NULL;
        fCount = 0;
    } else {
        int modeCount = hasModes ? count : 0;
        size_t size = sizeof(SkImageFilter*) * count + sizeof(uint8_t) * modeCount;
        if (size <= sizeof(fStorage)) {
            fFilters = SkTCast<SkImageFilter**>(fStorage);
        } else {
            fFilters = SkTCast<SkImageFilter**>(sk_malloc_throw(size));
        }
        fModes = hasModes ? SkTCast<uint8_t*>(fFilters + count) : NULL;
        fCount = count;
    }
}

void SkMergeImageFilter::init(SkImageFilter* const filters[], int count,
                              const SkXfermode::Mode modes[]) {
    this->initAlloc(count, !!modes);
    for (int i = 0; i < count; ++i) {
        fFilters[i] = SkSafeRefReturn(filters[i]);
        if (modes) {
            fModes[i] = SkToU8(modes[i]);
        }
    }
}

SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
                                       SkXfermode::Mode mode) {
    SkImageFilter* filters[] = { first, second };
    SkXfermode::Mode modes[] = { mode, mode };
    this->init(filters, 2, SkXfermode::kSrcOver_Mode == mode ? NULL : modes);
}

SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* const filters[], int count,
                                       const SkXfermode::Mode modes[]) {
    this->init(filters, count, modes);
}

SkMergeImageFilter::~SkMergeImageFilter() {
    for (int i = 0; i < fCount; ++i) {
        SkSafeUnref(fFilters[i]);
    }

    if (fFilters != SkTCast<SkImageFilter**>(fStorage)) {
        sk_free(fFilters);
        // fModes is allocated in the same block as fFilters, so no need to
        // separately free it.
    }
}

bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
                                        SkIRect* dst) {
    if (fCount < 1) {
        return false;
    }

    SkIRect totalBounds;

    for (int i = 0; i < fCount; ++i) {
        SkImageFilter* filter = fFilters[i];
        SkIRect r;
        if (filter) {
            if (!filter->filterBounds(src, ctm, &r)) {
                return false;
            }
        } else {
            r = src;
        }
        if (0 == i) {
            totalBounds = r;
        } else {
            totalBounds.join(r);
        }
    }

    // don't modify dst until now, so we don't accidentally change it in the
    // loop, but then return false on the next filter.
    *dst = totalBounds;
    return true;
}

bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                       const SkMatrix& ctm,
                                       SkBitmap* result, SkIPoint* loc) {
    if (fCount < 1) {
        return false;
    }

    const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(),
                                                src.width(), src.height());
    SkIRect bounds;
    if (!this->filterBounds(srcBounds, ctm, &bounds)) {
        return false;
    }

    const int x0 = bounds.left();
    const int y0 = bounds.top();

    SkDevice* dst = proxy->createDevice(bounds.width(), bounds.height());
    if (NULL == dst) {
        return false;
    }
    OwnDeviceCanvas canvas(dst);
    SkPaint paint;

    for (int i = 0; i < fCount; ++i) {
        SkBitmap tmp;
        const SkBitmap* srcPtr;
        SkIPoint pos = *loc;
        SkImageFilter* filter = fFilters[i];
        if (filter) {
            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
                return false;
            }
            srcPtr = &tmp;
        } else {
            srcPtr = &src;
        }

        if (fModes) {
            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
        } else {
            paint.setXfermode(NULL);
        }
        canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
    }

    loc->set(bounds.left(), bounds.top());
    *result = dst->accessBitmap(false);
    return true;
}

void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

    int storedCount = fCount;
    if (fModes) {
        // negative count signals we have modes
        storedCount = -storedCount;
    }
    buffer.write32(storedCount);

    if (fCount) {
        for (int i = 0; i < fCount; ++i) {
            buffer.writeFlattenable(fFilters[i]);
        }
        if (fModes) {
            buffer.write(fModes, fCount * sizeof(fModes[0]));
        }
    }
}

SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    int storedCount = buffer.readS32();
    this->initAlloc(SkAbs32(storedCount), storedCount < 0);

    for (int i = 0; i < fCount; ++i) {
        fFilters[i] = (SkImageFilter*)buffer.readFlattenable();
    }

    if (fModes) {
        SkASSERT(storedCount < 0);
        buffer.read(fModes, fCount * sizeof(fModes[0]));
    } else {
        SkASSERT(storedCount >= 0);
    }
}

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

#include "SkColorFilter.h"

SkColorFilterImageFilter::~SkColorFilterImageFilter() {
    SkSafeUnref(fColorFilter);
}

bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                             const SkMatrix& matrix,
                                             SkBitmap* result,
                                             SkIPoint* loc) {
    SkColorFilter* cf = fColorFilter;
    if (NULL == cf) {
        *result = src;
        return true;
    }

    SkDevice* dev = proxy->createDevice(src.width(), src.height());
    if (NULL == dev) {
        return false;
    }
    OwnDeviceCanvas canvas(dev);
    SkPaint paint;

    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    paint.setColorFilter(fColorFilter);
    canvas.drawSprite(src, 0, 0, &paint);

    *result = dev->accessBitmap(false);
    return true;
}

void SkColorFilterImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

    buffer.writeFlattenable(fColorFilter);
}

SkColorFilterImageFilter::SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    fColorFilter = (SkColorFilter*)buffer.readFlattenable();
}

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

bool SkDownSampleImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                            const SkMatrix& matrix,
                                            SkBitmap* result, SkIPoint*) {
    SkScalar scale = fScale;
    if (scale > SK_Scalar1 || scale <= 0) {
        return false;
    }

    int dstW = SkScalarRoundToInt(src.width() * scale);
    int dstH = SkScalarRoundToInt(src.height() * scale);
    if (dstW < 1) {
        dstW = 1;
    }
    if (dstH < 1) {
        dstH = 1;
    }

    SkBitmap tmp;

    // downsample
    {
        SkDevice* dev = proxy->createDevice(dstW, dstH);
        if (NULL == dev) {
            return false;
        }
        OwnDeviceCanvas canvas(dev);
        SkPaint paint;

        paint.setFilterBitmap(true);
        canvas.scale(scale, scale);
        canvas.drawBitmap(src, 0, 0, &paint);
        tmp = dev->accessBitmap(false);
    }

    // upscale
    {
        SkDevice* dev = proxy->createDevice(src.width(), src.height());
        if (NULL == dev) {
            return false;
        }
        OwnDeviceCanvas canvas(dev);

        SkRect r = SkRect::MakeWH(SkIntToScalar(src.width()),
                                  SkIntToScalar(src.height()));
        canvas.drawBitmapRect(tmp, NULL, r, NULL);
        *result = dev->accessBitmap(false);
    }
    return true;
}

void SkDownSampleImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

    buffer.writeScalar(fScale);
}

SkDownSampleImageFilter::SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    fScale = buffer.readScalar();
}

SK_DEFINE_FLATTENABLE_REGISTRAR(SkOffsetImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkComposeImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkMergeImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkColorFilterImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkDownSampleImageFilter)
<commit_msg>document OwnDeviceCanvas<commit_after>#include "SkTestImageFilters.h"
#include "SkCanvas.h"
#include "SkDevice.h"

// Simple helper canvas that "takes ownership" of the provided device, so that
// when this canvas goes out of scope, so will its device. Could be replaced
// with the following:
//
//  SkCanvas canvas(device);
//  SkAutoTUnref<SkDevice> aur(device);
//
class OwnDeviceCanvas : public SkCanvas {
public:
    OwnDeviceCanvas(SkDevice* device) : SkCanvas(device) {
        SkSafeUnref(device);
    }
};

bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                        const SkMatrix& matrix,
                                        SkBitmap* result,
                                        SkIPoint* loc) {
    SkVector vec;
    matrix.mapVectors(&vec, &fOffset, 1);

    loc->fX += SkScalarRoundToInt(vec.fX);
    loc->fY += SkScalarRoundToInt(vec.fY);
    *result = src;
    return true;
}

bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
                                         SkIRect* dst) {
    SkVector vec;
    ctm.mapVectors(&vec, &fOffset, 1);

    *dst = src;
    dst->offset(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY));
    return true;
}

void SkOffsetImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);
    buffer.writeScalar(fOffset.x());
    buffer.writeScalar(fOffset.y());
}

SkOffsetImageFilter::SkOffsetImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    fOffset.fX = buffer.readScalar();
    fOffset.fY = buffer.readScalar();
}

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

SkComposeImageFilter::~SkComposeImageFilter() {
    SkSafeUnref(fInner);
    SkSafeUnref(fOuter);
}

bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
                                         const SkBitmap& src,
                                         const SkMatrix& ctm,
                                         SkBitmap* result,
                                         SkIPoint* loc) {
    if (!fOuter && !fInner) {
        return false;
    }

    if (!fOuter || !fInner) {
        return (fOuter ? fOuter : fInner)->filterImage(proxy, src, ctm, result, loc);
    }

    SkBitmap tmp;
    return fInner->filterImage(proxy, src, ctm, &tmp, loc) &&
           fOuter->filterImage(proxy, tmp, ctm, result, loc);
}

bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
                                          const SkMatrix& ctm,
                                          SkIRect* dst) {
    if (!fOuter && !fInner) {
        return false;
    }

    if (!fOuter || !fInner) {
        return (fOuter ? fOuter : fInner)->filterBounds(src, ctm, dst);
    }

    SkIRect tmp;
    return fInner->filterBounds(src, ctm, &tmp) &&
           fOuter->filterBounds(tmp, ctm, dst);
}

void SkComposeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

    buffer.writeFlattenable(fOuter);
    buffer.writeFlattenable(fInner);
}

SkComposeImageFilter::SkComposeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    fOuter = (SkImageFilter*)buffer.readFlattenable();
    fInner = (SkImageFilter*)buffer.readFlattenable();
}

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

template <typename T> T* SkSafeRefReturn(T* obj) {
    SkSafeRef(obj);
    return obj;
}

void SkMergeImageFilter::initAlloc(int count, bool hasModes) {
    if (count < 1) {
        fFilters = NULL;
        fModes = NULL;
        fCount = 0;
    } else {
        int modeCount = hasModes ? count : 0;
        size_t size = sizeof(SkImageFilter*) * count + sizeof(uint8_t) * modeCount;
        if (size <= sizeof(fStorage)) {
            fFilters = SkTCast<SkImageFilter**>(fStorage);
        } else {
            fFilters = SkTCast<SkImageFilter**>(sk_malloc_throw(size));
        }
        fModes = hasModes ? SkTCast<uint8_t*>(fFilters + count) : NULL;
        fCount = count;
    }
}

void SkMergeImageFilter::init(SkImageFilter* const filters[], int count,
                              const SkXfermode::Mode modes[]) {
    this->initAlloc(count, !!modes);
    for (int i = 0; i < count; ++i) {
        fFilters[i] = SkSafeRefReturn(filters[i]);
        if (modes) {
            fModes[i] = SkToU8(modes[i]);
        }
    }
}

SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
                                       SkXfermode::Mode mode) {
    SkImageFilter* filters[] = { first, second };
    SkXfermode::Mode modes[] = { mode, mode };
    this->init(filters, 2, SkXfermode::kSrcOver_Mode == mode ? NULL : modes);
}

SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* const filters[], int count,
                                       const SkXfermode::Mode modes[]) {
    this->init(filters, count, modes);
}

SkMergeImageFilter::~SkMergeImageFilter() {
    for (int i = 0; i < fCount; ++i) {
        SkSafeUnref(fFilters[i]);
    }

    if (fFilters != SkTCast<SkImageFilter**>(fStorage)) {
        sk_free(fFilters);
        // fModes is allocated in the same block as fFilters, so no need to
        // separately free it.
    }
}

bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
                                        SkIRect* dst) {
    if (fCount < 1) {
        return false;
    }

    SkIRect totalBounds;

    for (int i = 0; i < fCount; ++i) {
        SkImageFilter* filter = fFilters[i];
        SkIRect r;
        if (filter) {
            if (!filter->filterBounds(src, ctm, &r)) {
                return false;
            }
        } else {
            r = src;
        }
        if (0 == i) {
            totalBounds = r;
        } else {
            totalBounds.join(r);
        }
    }

    // don't modify dst until now, so we don't accidentally change it in the
    // loop, but then return false on the next filter.
    *dst = totalBounds;
    return true;
}

bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                       const SkMatrix& ctm,
                                       SkBitmap* result, SkIPoint* loc) {
    if (fCount < 1) {
        return false;
    }

    const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(),
                                                src.width(), src.height());
    SkIRect bounds;
    if (!this->filterBounds(srcBounds, ctm, &bounds)) {
        return false;
    }

    const int x0 = bounds.left();
    const int y0 = bounds.top();

    SkDevice* dst = proxy->createDevice(bounds.width(), bounds.height());
    if (NULL == dst) {
        return false;
    }
    OwnDeviceCanvas canvas(dst);
    SkPaint paint;

    for (int i = 0; i < fCount; ++i) {
        SkBitmap tmp;
        const SkBitmap* srcPtr;
        SkIPoint pos = *loc;
        SkImageFilter* filter = fFilters[i];
        if (filter) {
            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
                return false;
            }
            srcPtr = &tmp;
        } else {
            srcPtr = &src;
        }

        if (fModes) {
            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
        } else {
            paint.setXfermode(NULL);
        }
        canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
    }

    loc->set(bounds.left(), bounds.top());
    *result = dst->accessBitmap(false);
    return true;
}

void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

    int storedCount = fCount;
    if (fModes) {
        // negative count signals we have modes
        storedCount = -storedCount;
    }
    buffer.write32(storedCount);

    if (fCount) {
        for (int i = 0; i < fCount; ++i) {
            buffer.writeFlattenable(fFilters[i]);
        }
        if (fModes) {
            buffer.write(fModes, fCount * sizeof(fModes[0]));
        }
    }
}

SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    int storedCount = buffer.readS32();
    this->initAlloc(SkAbs32(storedCount), storedCount < 0);

    for (int i = 0; i < fCount; ++i) {
        fFilters[i] = (SkImageFilter*)buffer.readFlattenable();
    }

    if (fModes) {
        SkASSERT(storedCount < 0);
        buffer.read(fModes, fCount * sizeof(fModes[0]));
    } else {
        SkASSERT(storedCount >= 0);
    }
}

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

#include "SkColorFilter.h"

SkColorFilterImageFilter::~SkColorFilterImageFilter() {
    SkSafeUnref(fColorFilter);
}

bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                             const SkMatrix& matrix,
                                             SkBitmap* result,
                                             SkIPoint* loc) {
    SkColorFilter* cf = fColorFilter;
    if (NULL == cf) {
        *result = src;
        return true;
    }

    SkDevice* dev = proxy->createDevice(src.width(), src.height());
    if (NULL == dev) {
        return false;
    }
    OwnDeviceCanvas canvas(dev);
    SkPaint paint;

    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    paint.setColorFilter(fColorFilter);
    canvas.drawSprite(src, 0, 0, &paint);

    *result = dev->accessBitmap(false);
    return true;
}

void SkColorFilterImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

    buffer.writeFlattenable(fColorFilter);
}

SkColorFilterImageFilter::SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    fColorFilter = (SkColorFilter*)buffer.readFlattenable();
}

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

bool SkDownSampleImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
                                            const SkMatrix& matrix,
                                            SkBitmap* result, SkIPoint*) {
    SkScalar scale = fScale;
    if (scale > SK_Scalar1 || scale <= 0) {
        return false;
    }

    int dstW = SkScalarRoundToInt(src.width() * scale);
    int dstH = SkScalarRoundToInt(src.height() * scale);
    if (dstW < 1) {
        dstW = 1;
    }
    if (dstH < 1) {
        dstH = 1;
    }

    SkBitmap tmp;

    // downsample
    {
        SkDevice* dev = proxy->createDevice(dstW, dstH);
        if (NULL == dev) {
            return false;
        }
        OwnDeviceCanvas canvas(dev);
        SkPaint paint;

        paint.setFilterBitmap(true);
        canvas.scale(scale, scale);
        canvas.drawBitmap(src, 0, 0, &paint);
        tmp = dev->accessBitmap(false);
    }

    // upscale
    {
        SkDevice* dev = proxy->createDevice(src.width(), src.height());
        if (NULL == dev) {
            return false;
        }
        OwnDeviceCanvas canvas(dev);

        SkRect r = SkRect::MakeWH(SkIntToScalar(src.width()),
                                  SkIntToScalar(src.height()));
        canvas.drawBitmapRect(tmp, NULL, r, NULL);
        *result = dev->accessBitmap(false);
    }
    return true;
}

void SkDownSampleImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
    this->INHERITED::flatten(buffer);

    buffer.writeScalar(fScale);
}

SkDownSampleImageFilter::SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
    fScale = buffer.readScalar();
}

SK_DEFINE_FLATTENABLE_REGISTRAR(SkOffsetImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkComposeImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkMergeImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkColorFilterImageFilter)
SK_DEFINE_FLATTENABLE_REGISTRAR(SkDownSampleImageFilter)
<|endoftext|>