/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "StdAfx.h" #include "ImageASC.h" #include "CryFile.h" //--------------------------------------------------------------------------- bool CImageASC::Save(const QString& fileName, const CFloatImage& image) { // There are two types of ARCGrid file formats - binary (ADF) and ASCII (ASC). // See here: https://en.wikipedia.org/wiki/Esri_grid uint32 width = image.GetWidth(); uint32 height = image.GetHeight(); float* pixels = image.GetData(); string fileHeader; fileHeader.Format( // Number of columns and rows in the data "ncols %d\n" "nrows %d\n" // The coordinates of the bottom-left corner. // These numbers represent coordinates on a globe, so this choice of values is arbitrary. "xllcorner 0.0\n" "yllcorner 0.0\n" // The size of each grid square. // The problem is that cellsize represents the size of a square on a grid being projected onto a globe. // This number can be used to convert size to degrees, based on where on the globe it appears. // We don't have a real-world location associated with our data, so this size choice is arbitrary. "cellsize 0.0003\n" // The value used for missing data. Since we shouldn't have any missing data, we'll choose a value that can't appear below. "nodata_value -1\n" , width, height); FILE* file = nullptr; azfopen(&file, fileName.toUtf8().data(), "wt"); if (!file) { return false; } // First print the file header fprintf(file, fileHeader.c_str()); // Then print all the pixels. for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { fprintf(file, "%.7f ", pixels[x + y * width]); } fprintf(file, "\n"); } fclose(file); return true; } //--------------------------------------------------------------------------- bool CImageASC::Load(const QString& fileName, CFloatImage& image) { FILE* file = nullptr; azfopen(&file, fileName.toUtf8().data(), "rt"); if (!file) { return false; } const char seps[] = " \r\n\t"; char* token; int32 width = 0; int32 height = 0; float nodataValue = 0.0f; bool validData = true; // Read the file into memory fseek(file, 0, SEEK_END); int fileSize = ftell(file); fseek(file, 0, SEEK_SET); char* str = new char[fileSize]; fread(str, fileSize, 1, file); // Break all of the values in the file apart into tokens. char* nextToken = nullptr; token = azstrtok(str, 0, seps, &nextToken); // ncols = grid width validData = validData && (azstricmp(token, "ncols") == 0); token = azstrtok(NULL, 0, seps, &nextToken); width = atoi(token); // nrows = grid height token = azstrtok(NULL, 0, seps, &nextToken); validData = validData && (azstricmp(token, "nrows") == 0); token = azstrtok(NULL, 0, seps, &nextToken); height = atoi(token); // xllcorner = leftmost coordinate. (Skip, we don't care about it) token = azstrtok(NULL, 0, seps, &nextToken); validData = validData && (azstricmp(token, "xllcorner") == 0); token = azstrtok(NULL, 0, seps, &nextToken); // yllcorner = bottommost coordinate. (Skip, we don't care about it) token = azstrtok(NULL, 0, seps, &nextToken); validData = validData && (azstricmp(token, "yllcorner") == 0); token = azstrtok(NULL, 0, seps, &nextToken); // cellsize = size of each grid cell. (Skip, we don't care about it) token = azstrtok(NULL, 0, seps, &nextToken); validData = validData && (azstricmp(token, "cellsize") == 0); token = azstrtok(NULL, 0, seps, &nextToken); // nodata_value = the value used for missing data. We'll replace these with 0 height. token = azstrtok(NULL, 0, seps, &nextToken); validData = validData && (azstricmp(token, "nodata_value") == 0); token = azstrtok(NULL, 0, seps, &nextToken); nodataValue = atof(token); if (!validData) { // Bad file. not supported asc. delete[]str; fclose(file); return false; } image.Allocate(width, height); // Read in the pixel data float* p = image.GetData(); int size = width * height; int i = 0; float pixelValue; float maxPixel = 0.0f; while (token != NULL && i < size) { token = azstrtok(NULL, 0, seps, &nextToken); if (token != NULL) { // Negative heights aren't supported, clamp to 0. pixelValue = max(0.0, atof(token)); // If this is a location we specifically don't have data for, set it to 0. if (pixelValue == nodataValue) { pixelValue = 0.0f; } *p++ = pixelValue; maxPixel = max(maxPixel, pixelValue); i++; } } if (maxPixel > 0.0f) { // Scale our range down to 0 - 1 float* p = image.GetData(); for (i = 0; i < size; i++) { p[i] = clamp_tpl(p[i] / maxPixel, 0.0f, 1.0f); } } delete[]str; fclose(file); return true; }