From 7f4c880147719d7b2d3cd97c68ad5af76764042b Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 30 Aug 2013 20:46:16 +0200 Subject: [PATCH] AnvilStats: Implemented a cImageComposingCallback class. This will ease the creation of callbacks that produce per-region images of stuff. --- Tools/AnvilStats/AnvilStats.vcproj | 8 + Tools/AnvilStats/ImageComposingCallback.cpp | 189 ++++++++++++++++++++ Tools/AnvilStats/ImageComposingCallback.h | 95 ++++++++++ 3 files changed, 292 insertions(+) create mode 100644 Tools/AnvilStats/ImageComposingCallback.cpp create mode 100644 Tools/AnvilStats/ImageComposingCallback.h diff --git a/Tools/AnvilStats/AnvilStats.vcproj b/Tools/AnvilStats/AnvilStats.vcproj index ed4ffa9a5..b6000ea3e 100644 --- a/Tools/AnvilStats/AnvilStats.vcproj +++ b/Tools/AnvilStats/AnvilStats.vcproj @@ -321,6 +321,14 @@ RelativePath=".\HeightMap.h" > + + + + diff --git a/Tools/AnvilStats/ImageComposingCallback.cpp b/Tools/AnvilStats/ImageComposingCallback.cpp new file mode 100644 index 000000000..138821698 --- /dev/null +++ b/Tools/AnvilStats/ImageComposingCallback.cpp @@ -0,0 +1,189 @@ + +// ImageComposingCallback.cpp + +// Implements the cImageComposingCallback class that implements a subset of cCallback for composing per-region images + +#include "Globals.h" +#include "ImageComposingCallback.h" + + + + + +cImageComposingCallback::cImageComposingCallback(const AString & a_FileNamePrefix) : + m_FileNamePrefix(a_FileNamePrefix), + m_CurrentRegionX(INVALID_REGION_COORD), + m_CurrentRegionZ(INVALID_REGION_COORD), + m_ImageData(new int[32 * 16 * 32 * 16]) +{ +} + + + + + +cImageComposingCallback::~cImageComposingCallback() +{ + delete[] m_ImageData; +} + + + + + +bool cImageComposingCallback::OnNewRegion(int a_RegionX, int a_RegionZ) +{ + ASSERT(m_CurrentRegionX == INVALID_REGION_COORD); + ASSERT(m_CurrentRegionZ == INVALID_REGION_COORD); // Has any previous region been finished properly? + + m_CurrentRegionX = a_RegionX; + m_CurrentRegionZ = a_RegionZ; + OnEraseImage(); + + return CALLBACK_CONTINUE; +} + + + + + +void cImageComposingCallback::OnRegionFinished(int a_RegionX, int a_RegionZ) +{ + ASSERT(m_CurrentRegionX != INVALID_REGION_COORD); + ASSERT(m_CurrentRegionZ != INVALID_REGION_COORD); // Has a region been started properly? + ASSERT(m_CurrentRegionX == a_RegionX); + ASSERT(m_CurrentRegionZ == a_RegionZ); // Is it the same region that has been started? + + AString FileName = GetFileName(a_RegionX, a_RegionZ); + if (!FileName.empty()) + { + OnBeforeImageSaved(a_RegionX, a_RegionZ, FileName); + SaveImage(FileName); + OnAfterImageSaved(a_RegionX, a_RegionZ, FileName); + } + + m_CurrentRegionX = INVALID_REGION_COORD; + m_CurrentRegionZ = INVALID_REGION_COORD; +} + + + + + +AString cImageComposingCallback::GetFileName(int a_RegionX, int a_RegionZ) +{ + return Printf("%s.%d.%d", m_FileNamePrefix.c_str(), a_RegionX, a_RegionZ); +} + + + + + +void cImageComposingCallback::OnEraseImage(void) +{ + // By default erase the image to black: + EraseImage(0); +} + + + + + +void cImageComposingCallback::EraseImage(int a_Color) +{ + for (int i = 0; i < PIXEL_COUNT; i++) + { + m_ImageData[i] = a_Color; + } +} + + + + + +void cImageComposingCallback::EraseChunk(int a_Color, int a_RelChunkX, int a_RelChunkZ) +{ + int Base = a_RelChunkZ * IMAGE_HEIGHT + a_RelChunkX * 16; + for (int v = 0; v < 16; v++) + { + int BaseV = Base + v * IMAGE_HEIGHT; + for (int u = 0; u < 16; u++) + { + m_ImageData[BaseV + u] = a_Color; + } + } // for y +} + + + + + +void cImageComposingCallback::SetPixel(int a_RelU, int a_RelV, int a_Color) +{ + ASSERT((a_RelU >= 0) && (a_RelU < IMAGE_WIDTH)); + ASSERT((a_RelV >= 0) && (a_RelV < IMAGE_HEIGHT)); + + m_ImageData[a_RelU + IMAGE_WIDTH * a_RelV] = a_Color; +} + + + + + +int cImageComposingCallback::GetPixel(int a_RelU, int a_RelV) +{ + if ((a_RelU < 0) || (a_RelU >= IMAGE_WIDTH) || (a_RelV < 0) || (a_RelV >= IMAGE_HEIGHT)) + { + // Outside the image data + return -1; + } + + return m_ImageData[a_RelU + IMAGE_WIDTH * a_RelV]; +} + + + + + + +void cImageComposingCallback::SetPixelURow(int a_RelUStart, int a_RelV, int a_CountU, int * a_Pixels) +{ + ASSERT((a_RelUStart >= 0) && (a_RelUStart + a_CountU < IMAGE_WIDTH)); + ASSERT((a_RelV >= 0) && (a_RelV < IMAGE_HEIGHT)); + ASSERT(a_Pixels != NULL); + + int Base = a_RelUStart + a_RelV * IMAGE_WIDTH; + for (int u = 0; u < a_CountU; u++) + { + m_ImageData[Base + u] = a_Pixels[u]; + } +} + + + + + +void cImageComposingCallback::SaveImage(const AString & a_FileName) +{ + cFile f(a_FileName, cFile::fmWrite); + if (!f.IsOpen()) + { + return; + } + + // Header for BMP files (is the same for the same-size files) + static const unsigned char BMPHeader[] = + { + 0x42, 0x4D, 0x36, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + } ; + + f.Write(BMPHeader, sizeof(BMPHeader)); + f.Write(m_ImageData, PIXEL_COUNT * 4); +} + + + + diff --git a/Tools/AnvilStats/ImageComposingCallback.h b/Tools/AnvilStats/ImageComposingCallback.h new file mode 100644 index 000000000..c04dc869f --- /dev/null +++ b/Tools/AnvilStats/ImageComposingCallback.h @@ -0,0 +1,95 @@ + +// ImageComposingCallback + +// Declares the cImageComposingCallback class that implements a subset of cCallback for composing per-region images + + + + + +#pragma once + +#include "Callback.h" + + + + +/** Implements the plumbing for composing per-region images from multiple chunks. +To use this class, create a descendant that writes the image data using +SetPixel() or SetPixelURow() functions. + +For the purpose of this class the image data is indexed U (horz) * V (vert), to avoid confusion with other coords. +The image is a 32bpp raw imagedata, written into a BMP file. +*/ +class cImageComposingCallback : + public cCallback +{ +public: + enum + { + INVALID_REGION_COORD = 99999, ///< Used for un-assigned region coords + IMAGE_WIDTH = 32 * 16, + IMAGE_HEIGHT = 32 * 16, + PIXEL_COUNT = IMAGE_WIDTH * IMAGE_HEIGHT, ///< Total pixel count of the image data + } ; + + cImageComposingCallback(const AString & a_FileNamePrefix); + virtual ~cImageComposingCallback(); + + // cCallback overrides: + virtual bool OnNewRegion(int a_RegionX, int a_RegionZ) override; + virtual void OnRegionFinished(int a_RegionX, int a_RegionZ) override; + + // New introduced overridable functions: + + /// Called when a file is about to be saved, to generate the filename + virtual AString GetFileName(int a_RegionX, int a_RegionZ); + + /// Called before the file is saved + virtual void OnBeforeImageSaved(int a_RegionX, int a_RegionZ, const AString & a_FileName) {} + + /// Called after the image is saved to a file + virtual void OnAfterImageSaved(int a_RegionX, int a_RegionZ, const AString & a_FileName) {} + + /// Called when a new region is beginning, to erase the image data + virtual void OnEraseImage(void); + + // Functions for manipulating the image: + + /// Erases the entire image with the specified color + void EraseImage(int a_Color); + + /// Erases the specified chunk's portion of the image with the specified color. Note that chunk coords are relative to the current region + void EraseChunk(int a_Color, int a_RelChunkX, int a_RelChunkZ); + + /// Returns the current region X coord + int GetCurrentRegionX(void) const { return m_CurrentRegionX; } + + /// Returns the current region Z coord + int GetCurrentRegionZ(void) const { return m_CurrentRegionZ; } + + /// Sets the pixel at the specified UV coords to the specified color + void SetPixel(int a_RelU, int a_RelV, int a_Color); + + /// Returns the color of the pixel at the specified UV coords; -1 if outside + int GetPixel(int a_RelU, int a_RelV); + + /// Sets a row of pixels. a_Pixels is expected to be a_CountU pixels wide. a_RelUStart + a_CountU is assumed less than image width + void SetPixelURow(int a_RelUStart, int a_RelV, int a_CountU, int * a_Pixels); + +protected: + /// Prefix for the filenames, when generated by the default GetFileName() function + AString m_FileNamePrefix; + + /// Coords of the currently processed region + int m_CurrentRegionX, m_CurrentRegionZ; + + /// Raw image data; 1 MiB worth of data, therefore unsuitable for stack allocation. [u + IMAGE_WIDTH * v] + int * m_ImageData; + + void SaveImage(const AString & a_FileName); +} ; + + + +