I am trying to compile a project using make command.
I have several occured errors about namespace std::.
I am using Ubuntu and I think I've installed all I need to compile C++ language.
Here's the code:
#include "texture.hpp"
#include "texturefetch.hpp"
#include <png.h>
#include <cstdio>
#include <iostream>
#include <cmath>
#include <assert.h>
#include <string>
Texture::~Texture(){
delete[] mPixels;
for (int i=1; i<mlodmax+1; ++i)
delete[] mMipMapLevels[i];
delete[] mMipMapLevels;
}
bool Texture::getclamp() const
{
return clamp;
}
void Texture::setclamp(bool value)
{
clamp = value;
}
int Texture::load(std::string filename){
FILE *fp;
png_byte magic[8];
/* open image file */
fp = std::fopen (filename.c_str(), "rb");
if (!fp) {
fprintf (stderr, "error: couldn't open \"%s\"!\n", filename.c_str());
return 0;
}
/* read magic number */
fread (magic, 1, sizeof (magic), fp);
/* check for valid magic number */
if (!png_check_sig (magic, sizeof (magic))) {
fprintf (stderr, "error: \"%s\" is not a valid PNG image!\n", filename.c_str());
fclose (fp);
return 0;
}
/* create a png read struct */
png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
fclose (fp);
return 0;
}
/* create a png info struct */
png_infop info_ptr = png_create_info_struct (png_ptr);
if (!info_ptr) {
fclose (fp);
png_destroy_read_struct (&png_ptr, NULL, NULL);
return 0;
}
/* initialize the setjmp for returning properly after a libpng
error occured */
if (setjmp (png_jmpbuf (png_ptr))) {
fclose (fp);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return 0;
}
/* setup libpng for using standard C fread() function
with our FILE pointer */
png_init_io (png_ptr, fp);
/* tell libpng that we have already read the magic number */
png_set_sig_bytes (png_ptr, sizeof (magic));
/* read png info */
png_read_info (png_ptr, info_ptr);
int bit_depth, color_type;
/* get some usefull information from header */
bit_depth = png_get_bit_depth (png_ptr, info_ptr);
color_type = png_get_color_type (png_ptr, info_ptr);
/* convert index color images to RGB images */
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb (png_ptr);
/* convert 1-2-4 bits grayscale images to 8 bits
grayscale. */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png_ptr);
if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha (png_ptr);
if (bit_depth == 16)
png_set_strip_16 (png_ptr);
else if (bit_depth < 8)
png_set_packing (png_ptr);
/* update info structure to apply transformations */
png_read_update_info (png_ptr, info_ptr);
/* retrieve updated information */
png_get_IHDR (png_ptr, info_ptr,
(png_uint_32*)(&mWidth),
(png_uint_32*)(&mHeight),
&bit_depth, &color_type,
NULL, NULL, NULL);
switch (color_type) {
case PNG_COLOR_TYPE_GRAY:
mPixelFormat = 1;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
mPixelFormat = 2;
break;
case PNG_COLOR_TYPE_RGB:
mPixelFormat = 3;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
mPixelFormat = 4;
break;
default:
/* Badness */
break;
}
/* we can now allocate memory for storing pixel data */
mPixels = new unsigned char[mWidth * mHeight * mPixelFormat];
png_bytep *row_pointers;
/* setup a pointer array. Each one points at the begening of a row. */
row_pointers = new png_bytep[mHeight];
for (int i = 0; i < mHeight; ++i) {
row_pointers[i] = (png_bytep)(mPixels +
((mHeight - (i + 1)) * mWidth * mPixelFormat));
}
/* read pixel data using row pointers */
png_read_image (png_ptr, row_pointers);
/* finish decompression and release memory */
png_read_end (png_ptr, NULL);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
fclose (fp);
/* we don't need row pointers anymore */
delete[] row_pointers;
return 1;
}
Texture::Texture(std::string filename) : mPixels(NULL), mMipMapLevels(NULL), hasmipmap(false), mSu(1.f), mSv(1.f), clamp(false){
if (!load(filename)) {
std::cerr << "Erreur : impossible de lire le fichier " << filename << std::endl;
} else {
mlodmax = (int)std::floor(std::min(std::log2(mWidth), std::log2(mHeight)));
}
}
bool Texture::prefilter(){
hasmipmap = buildmipmaps();
return hasmipmap;
}
bool Texture::buildmipmaps(){
mMipMapLevels = new unsigned char*[mlodmax+1];
mMipMapLevels[0] = mPixels;
for (int i=1;i<mlodmax+1; ++i)
mMipMapLevels[i]=NULL;
// Call of the student function
return prefilterTexture(mMipMapLevels, mWidth, mHeight, mPixelFormat, mlodmax+1);
}
void Texture::setScale(float su, float sv){
mSu = 1.f/su;
mSv = 1.f/sv;
}
void Texture::getPixel(float u, float v, float lod, Color &color){
if (clamp){
u = (u>0) ? ((u<1) ? u : 1) : 0;
v = (v>0) ? ((v<1) ? v : 1) : 0;
}
u = mSu*u;
v = mSv*v;
float scaledU = u * (mWidth - 1);
float scaledV = v * (mHeight - 1);
// color = Color(u,v,0);
// return;
int s = ((int)scaledU) % mWidth;
int t = ((int)scaledV) % mHeight;
float ds = scaledU - std::floor(scaledU);
float dt = scaledV - std::floor(scaledV);
if (s<0){
s = s+mWidth;
}
if (t<0){
t = t+mHeight;
}
// Nearest
// color = interpolate(mPixels, mWidth, mHeight, s,t, 0.f,0.f);
// return;
// Linear
// color = interpolate(mPixels, mWidth, mHeight, s,t, ds, dt);
// return;
/* Filtrage */
lod = std::min(lod-1, (float)mlodmax);
if (lod > 0) {
// color = Color(0, std::floor(lod), 0);
// return;
int baselod = (int)std::floor(lod);
int pix = (int)(std::pow(2.f,baselod)); // bigger inferior mipmap level
if (hasmipmap){
// Sous-echantillonnage --> filtrage mip-map
Color color1 = integrateMipMap(baselod, ((float)s + ds)/pix, ((float)t + dt)/pix, mWidth/pix, mHeight/pix);
if (baselod < mlodmax){
pix = std::pow(2.f,baselod+1); // smaller superior mipmap level
Color color2 = integrateMipMap(baselod+1, ((float)s + ds)/pix, ((float)t + dt)/pix, mWidth/pix, mHeight/pix);
float dm = lod - std::floor(lod);
color = color1 * (1-dm) + color2 * dm;
} else {
color = color1;
}
} else {
int cs = pix * (s/pix);
int ct = pix * (t/pix);
Color color1 = integrateExplicit(mMipMapLevels[0], mWidth, mHeight, mPixelFormat, cs, ct, pix, pix);
if (baselod < mlodmax){
pix = std::pow(2.f,baselod+1); // smaller superior mipmap level
cs = pix * (s/pix);
ct = pix * (t/pix);
Color color2 = integrateExplicit(mMipMapLevels[0], mWidth, mHeight, mPixelFormat, cs, ct, pix, pix);
float dm = lod - std::floor(lod);
color = color1 * (1-dm) + color2 * dm;
} else {
color = color1;
}
}
} else {
// color = Color(1/*fabsf(lod)*/, 0, 0);
// return;
color = interpolate(mPixels, mWidth, mHeight, s,t, ds,dt);
}
}
Color Texture::integrateMipMap(int level, float s, float t, int w, int h){
Color color(0.f, 0.f, 0.f);
color = interpolate(mMipMapLevels[level], w, h, (int)s, (int)t, s-std::floor(s), t-std::floor(t));
return color;
}
Color Texture::integrateExplicit(unsigned char *pixels, int imageWidth, int imageHeight, int pixelFormat, float s, float t, int w, int h){
#if 0
Color color(0.f, 0.f, 0.f);
for (int i=0; i<h; i++)
for (int j=0; j<w; j++) {
int k = ((int)(t+i)) % imageHeight;
int l = ((int)(s+j)) % imageWidth;
int linPos = (k*imageWidth + l)*(pixelFormat);
Color centerColor(((float) pixels[linPos])/255.0, ((float) pixels[linPos+1])/255.0, ((float) pixels[linPos+2])/255.0);
color = color + centerColor;
}
color = color * (1.f/(w*h));
return color;
#endif
// Call of the student function
return integrateTexture(pixels, imageWidth, imageHeight, pixelFormat, s, t, w, h);
}
Color Texture::interpolate(unsigned char *pixels, int w, int h, int s, int t, float ds, float dt){
#if 0
Color color;
// Sur échantillonnage --> interpolation
// nearest texel
int linPos = (t*w + s)*(mPixelFormat);
Color centerColor(((float) pixels[linPos])/255.0, ((float) pixels[linPos+1])/255.0, ((float) pixels[linPos+2])/255.0);
Color rightColor;
int r = std::min(s+1, w-1);
int posds = (t*w + r)*(mPixelFormat);
rightColor = Color(
((float) pixels[posds])/255.0,
((float) pixels[posds+1])/255.0,
((float) pixels[posds+2])/255.0
);
Color upColor;
int p = std::min(t+1, h-1);
int posdt = (p*w + s)*(mPixelFormat);
upColor = Color (
((float) pixels[posdt])/255.0,
((float) pixels[posdt+1])/255.0,
((float) pixels[posdt+2])/255.0
);
Color upRightColor;
int posdtds = (p*w + r)*(mPixelFormat);
upRightColor = Color (
((float) pixels[posdtds])/255.0,
((float) pixels[posdtds+1])/255.0,
((float) pixels[posdtds+2])/255.0
);
rightColor = centerColor*(1-ds) + rightColor*ds;
upColor = upColor*(1-ds) + upRightColor*ds;
color = rightColor*(1-dt) + upColor*dt;
return color;
#endif
// Call of the student function
return interpolateTexture(pixels, w, h, mPixelFormat, (float)s+ds, (float)t+dt);
}
And here's errors in terminal:
In file included from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:1:0:
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp: In member function ‘float Texture::computeMipmapLevel(const Vector3D&, const Vector3D&)’:
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:55:16: error: ‘log2’ is not a member of ‘std’
return std::log2(std::max(dTdx.norm()*width()*mSu, dTdy.norm()*height()*mSv));
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:55:16: note: suggested alternative:
In file included from /usr/include/features.h:374:0,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
from /usr/include/c++/4.8/cmath:41,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/vector3d.hpp:3,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:4,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:1:
/usr/include/x86_64-linux-gnu/bits/math-finite.h:344:15: note: ‘log2’
extern double __REDIRECT_NTH (log2, (double), __log2_finite);
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp: In constructor ‘Texture::Texture(std::string)’:
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:162:44: error: ‘log2’ is not a member of ‘std’
mlodmax = (int)std::floor(std::min(std::log2(mWidth), std::log2(mHeight)));
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:162:44: note: suggested alternative:
In file included from /usr/include/features.h:374:0,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
from /usr/include/c++/4.8/cmath:41,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/vector3d.hpp:3,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:4,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:1:
/usr/include/x86_64-linux-gnu/bits/math-finite.h:344:15: note: ‘log2’
extern double __REDIRECT_NTH (log2, (double), __log2_finite);
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:162:63: error: ‘log2’ is not a member of ‘std’
mlodmax = (int)std::floor(std::min(std::log2(mWidth), std::log2(mHeight)));
^
/home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:162:63: note: suggested alternative:
In file included from /usr/include/features.h:374:0,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
from /usr/include/c++/4.8/cmath:41,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/vector3d.hpp:3,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.hpp:4,
from /home/celine/OIM-LancerDeRayons-2014/coreraytracer/texture.cpp:1:
/usr/include/x86_64-linux-gnu/bits/math-finite.h:344:15: note: ‘log2’
extern double __REDIRECT_NTH (log2, (double), __log2_finite);
I took a look on internet and I tried to include and but it's still not working...
Thanks!
log2 is a C99 function. It's not in C++98, and therefore not in the std namespace. Either compile in C++11 mode (C++11 is aligned with C99):
// g++ -std=c++11
#include <cmath>
#include <iostream>
int main()
{
std::cout << std::log2(3) << std::endl;
}
... or get the function from the C header, without the namespace:
// g++ -std=c++98
#include <iostream>
#include <math.h>
int main()
{
std::cout << log2(3) << std::endl;
}
Related
The shader takes an SSBO of Photons that have a position, direction, wavelength and intensity and each thread is responsible for tracing exactly one photon through the grid, where at each grid cell the photon hits, the intensity is accumulated for each wavelength to create a spectral distribution for each grid cell.
The problem is that the shader works perfectly for 100,000 photons, but doesn't return a result for 1,000,000 photons.
I looked into the sizes for the SSBOs and all were within my GPUs (NVIDIA Quadro P6000) limits of 2GB:
SSBO Grid size: 1.5GB
SSBO Photons Size: 0.02GB
If I change the logic at some places it works with one million photons (see lines 87 and 114 for comments).
I currently can't see any explanation of why the shader fails for 1,000,000 photons, but works for 100,000 photons. The logic is the same and the buffer sizes are within limits. (That the buffer size can't be a problem is also confirmed by that it works when changing the logic.)
Below is the source code. If you want to try it yourself here is the code on github: https://github.com/TheJhonny007/TextureTracerDebug
Compute Shader:
#version 430
#extension GL_EXT_compute_shader: enable
#extension GL_EXT_shader_storage_buffer_object: enable
#extension GL_ARB_compute_variable_group_size: enable
const uint TEX_WIDTH = 1024u;
const uint TEX_HEIGHT = TEX_WIDTH;
const uint MIN_WAVELENGTH = 380u;
const uint MAX_WAVELENGTH = 740u;
const uint NUM_WAVELENGTHS = MAX_WAVELENGTH - MIN_WAVELENGTH;
// Size: 24 bytes -> ~40,000,000 photons per available gigabyte of ram
struct Photon {
vec2 position;// m
vec2 direction;// normalized
uint wavelength;// nm
float intensity;// 0..1 should start at 1
};
layout(std430, binding = 0) buffer Photons {
Photon photons[];
};
// Size: 1440 bytes -> ~700,000 pixels per available gigabyte of ram
struct Pixel {
uint intensityAtWavelengths[NUM_WAVELENGTHS];// [0..1000]
};
layout(std430, binding = 1) buffer Pixels {
//Pixel pixels[TEX_WIDTH][TEX_HEIGHT];
// NVIDIAs linker takes ages to link if the sizes are specified :(
Pixel[] pixels;
};
uniform float xAxisScalingFactor;
vec2 getHorizontalRectangleAt(int i) {
float x = pow(float(i), xAxisScalingFactor);
float w = pow(float(i + 1), xAxisScalingFactor);
return vec2(x, w);
}
uniform float rectangleHeight;
struct Rectangle {
float x;
float y;
float w;
float h;
};
layout (local_size_variable) in;
void addToPixel(uvec2 idx, uint wavelength, uint intensity) {
if (idx.x >= 0u && idx.x < TEX_WIDTH && idx.y >= 0u && idx.y < TEX_HEIGHT) {
uint index = (idx.y * TEX_WIDTH) + idx.x;
atomicAdd(pixels[index].intensityAtWavelengths[wavelength - MIN_WAVELENGTH], intensity);
}
}
/// Returns the rectangle at the given indices.
Rectangle getRectangleAt(ivec2 indices) {
vec2 horRect = getHorizontalRectangleAt(indices.x);
return Rectangle(horRect.x, rectangleHeight * float(indices.y), horRect.y, rectangleHeight);
}
uniform float shadowLength;
uniform float shadowHeight;
/// Returns the indices of the rectangle at the given location
ivec2 getRectangleIdxAt(vec2 location) {
int x = 0;
int y = int(location.y / rectangleHeight);
return ivec2(x, y);
}
float getRayIntersectAtX(Photon ray, float x) {
float slope = ray.direction.y / ray.direction.x;
return slope * (x - ray.position.x) + ray.position.y;
}
ivec2 getRayRectangleExitEdge(Photon ray, Rectangle rect) {
float intersectHeight = getRayIntersectAtX(ray, rect.x + rect.w);
// IF ONE OF THE FIRST TWO CONDITIONS GETS REMOVED IT WORKS WITH 1'000'000 PHOTONS OTHERWISE ONLY 100'000 WHY?
if (intersectHeight < rect.y) {
return ivec2(0, -1);
} else if (intersectHeight > rect.y + rect.h) {
return ivec2(0, 1);
} else {
return ivec2(1, 0);
}
}
void main() {
uint gid = gl_GlobalInvocationID.x;
if (gid >= photons.length()) return;
Photon photon = photons[gid];
ivec2 photonTexIndices = getRectangleIdxAt(photon.position);
while (photonTexIndices.x < TEX_WIDTH && photonTexIndices.y < TEX_HEIGHT &&
photonTexIndices.x >= 0 && photonTexIndices.y >= 0) {
// need to convert to uint for atomic add operations...
addToPixel(uvec2(photonTexIndices), photon.wavelength, uint(photon.intensity * 100.0));
ivec2 dir = getRayRectangleExitEdge(photon, getRectangleAt(photonTexIndices));
photonTexIndices += dir;
// When the ray goes out of bounds on the bottom then mirror it to simulate rays coming from
// the other side of the planet. This works because of the rotational symmetry of the system.
// IF COMMENTET OUT IT WORKS WITH 1'000'000 PHOTONS OTHERWISE ONLY 100'000 WHY?
if (photonTexIndices.y < 0) {
photonTexIndices.y = 0;
photon.position.y *= -1.0;
photon.direction.y *= -1.0;
}
}
}
Tracer.hpp
#ifndef TEXTURE_TRACER_HPP
#define TEXTURE_TRACER_HPP
#include <glm/glm.hpp>
#include <random>
namespace gpu {
// 6 * 4 = 24 Bytes
struct Photon {
glm::vec2 position; // m
glm::vec2 direction; // normalized
uint32_t waveLength; // nm
float intensity; // 0..1 should start at 1
};
class TextureTracer {
public:
TextureTracer();
uint32_t createShadowMap(size_t numPhotons);
private:
void initTextureTracer();
void traceThroughTexture(uint32_t ssboPhotons, size_t numPhotons);
Photon emitPhoton();
std::vector<Photon> generatePhotons(uint32_t count);
struct {
uint32_t uRectangleHeight;
uint32_t uShadowLength;
uint32_t uShadowHeight;
uint32_t uXAxisScalingFactor;
} mTextureTracerUniforms;
uint32_t mTextureTracerProgram;
std::mt19937_64 mRNG;
std::uniform_real_distribution<> mDistributionSun;
std::uniform_int_distribution<uint32_t> mDistributionWavelength;
std::bernoulli_distribution mDistributionBoolean;
};
} // namespace gpu
#endif // TEXTURE_TRACER_HPP
Tracer.cpp
#include "TextureTracer.hpp"
#include <GL/glew.h>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <random>
#include <string>
#include <vector>
void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length,
const GLchar *message, const void *userParam) {
if (type == GL_DEBUG_TYPE_ERROR)
fprintf(stderr, "GL ERROR: type = 0x%x, severity = 0x%x, message = %s\n",
type, severity, message);
else
fprintf(stdout, "GL INFO: type = 0x%x, severity = 0x%x, message = %s\n",
type, severity, message);
}
namespace gpu {
const double TEX_HEIGHT_TO_RADIUS_FACTOR = 4;
const double TEX_SHADOW_LENGTH_FACTOR = 8;
const uint32_t TEX_WIDTH = 1024u;
const uint32_t TEX_HEIGHT = TEX_WIDTH;
const double RADIUS = 6'371'000.0;
const double RADIUS_FACTORED = RADIUS * TEX_HEIGHT_TO_RADIUS_FACTOR;
const double SUN_RADIUS = 695'510'000.0;
const double DIST_TO_SUN = 149'600'000'000.0;
const double ATMO_HEIGHT = 42'000.0;
std::string loadShader(const std::string &fileName) {
std::ifstream shaderFileStream(fileName, std::ios::in);
if (!shaderFileStream.is_open()) {
std::cerr << "Could not load the GLSL shader from '" << fileName << "'!"
<< std::endl;
exit(-1);
}
std::string shaderCode;
while (!shaderFileStream.eof()) {
std::string line;
std::getline(shaderFileStream, line);
shaderCode.append(line + "\n");
}
return shaderCode;
}
void TextureTracer::initTextureTracer() {
mTextureTracerProgram = glCreateProgram();
uint32_t rayTracingComputeShader = glCreateShader(GL_COMPUTE_SHADER);
std::string code = loadShader("../resources/TextureTracer.glsl");
const char *shader = code.c_str();
glShaderSource(rayTracingComputeShader, 1, &shader, nullptr);
glCompileShader(rayTracingComputeShader);
glAttachShader(mTextureTracerProgram, rayTracingComputeShader);
glLinkProgram(mTextureTracerProgram);
mTextureTracerUniforms.uRectangleHeight =
glGetUniformLocation(mTextureTracerProgram, "rectangleHeight");
mTextureTracerUniforms.uShadowHeight =
glGetUniformLocation(mTextureTracerProgram, "shadowHeight");
mTextureTracerUniforms.uShadowLength =
glGetUniformLocation(mTextureTracerProgram, "shadowLength");
mTextureTracerUniforms.uXAxisScalingFactor =
glGetUniformLocation(mTextureTracerProgram, "xAxisScalingFactor");
glDetachShader(mTextureTracerProgram, rayTracingComputeShader);
glDeleteShader(rayTracingComputeShader);
}
TextureTracer::TextureTracer()
: mRNG(1L), mDistributionSun(
std::uniform_real_distribution<>(-SUN_RADIUS, SUN_RADIUS)),
mDistributionWavelength(
std::uniform_int_distribution<uint32_t>(380, 739)),
mDistributionBoolean(std::bernoulli_distribution(0.5)) {
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(MessageCallback, nullptr);
initTextureTracer();
}
double raySphereDistance(glm::dvec2 origin, glm::dvec2 direction,
glm::dvec2 center, double radius) {
glm::dvec2 m = origin - center;
double b = glm::dot(m, direction);
double c = glm::dot(m, m) - (radius * radius);
if (c > 0.0 && b > 0.0)
return -1.0;
double discr = b * b - c;
// A negative discriminant corresponds to ray missing sphere
if (discr < 0.0)
return -1.0;
// Ray now found to intersect sphere, compute smallest t value of intersection
return glm::max(0.0, -b - glm::sqrt(discr));
}
Photon TextureTracer::emitPhoton() {
std::uniform_real_distribution<> distributionEarth(0.0, ATMO_HEIGHT);
glm::dvec2 target = {0.0, RADIUS + distributionEarth(mRNG)};
double d;
do {
d = glm::length(glm::dvec2(mDistributionSun(mRNG), mDistributionSun(mRNG)));
} while (d > SUN_RADIUS);
glm::dvec2 startPosition =
glm::dvec2(-DIST_TO_SUN, mDistributionBoolean(mRNG) ? d : -d);
glm::dvec2 direction = glm::normalize(target - startPosition);
startPosition +=
direction * raySphereDistance(startPosition, direction, {0.0, 0.0},
RADIUS + ATMO_HEIGHT);
return {glm::vec2(0.0, startPosition.y), glm::vec2(direction),
mDistributionWavelength(mRNG), 1.0f};
}
std::vector<Photon> TextureTracer::generatePhotons(uint32_t count) {
std::vector<Photon> photons(count);
std::generate(photons.begin(), photons.end(),
[this]() { return emitPhoton(); });
return photons;
}
void TextureTracer::traceThroughTexture(uint32_t ssboPhotons,
size_t numPhotons) {
glUseProgram(mTextureTracerProgram);
glUniform1f(mTextureTracerUniforms.uRectangleHeight,
RADIUS_FACTORED / TEX_HEIGHT);
const double shadowLength =
TEX_SHADOW_LENGTH_FACTOR * (DIST_TO_SUN * RADIUS) / (SUN_RADIUS - RADIUS);
glUniform1f(mTextureTracerUniforms.uShadowLength, shadowLength);
glUniform1f(mTextureTracerUniforms.uShadowHeight, RADIUS_FACTORED);
const double xAxisScalingFactor =
glm::log(shadowLength) / glm::log(static_cast<double>(TEX_WIDTH));
glUniform1f(mTextureTracerUniforms.uXAxisScalingFactor,
static_cast<float>(xAxisScalingFactor));
const uint32_t MIN_WAVELENGTH = 380u;
const uint32_t MAX_WAVELENGTH = 740u;
const uint32_t NUM_WAVELENGTHS = MAX_WAVELENGTH - MIN_WAVELENGTH;
size_t pixelBufferSize =
TEX_WIDTH * TEX_HEIGHT * NUM_WAVELENGTHS * sizeof(uint32_t);
uint32_t ssboPixels;
glGenBuffers(1, &ssboPixels);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboPixels);
glBufferData(GL_SHADER_STORAGE_BUFFER, pixelBufferSize, nullptr,
GL_DYNAMIC_COPY);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssboPhotons);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssboPixels);
const uint32_t numThreads = 32u;
const uint32_t numBlocks = numPhotons / numThreads;
std::cout << "numBlocks: " << numBlocks << std::endl;
glDispatchComputeGroupSizeARB(numBlocks, 1, 1, numThreads, 1, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
struct Pixel {
uint32_t intensityAtWavelengths[NUM_WAVELENGTHS];
};
std::vector<Pixel> pixels(TEX_WIDTH * TEX_HEIGHT);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboPixels);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, pixelBufferSize,
pixels.data());
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
for (int y = 0; y < TEX_HEIGHT; ++y) {
printf("%4i | ", y);
for (int x = 0; x < TEX_WIDTH; ++x) {
Pixel p = pixels[y * TEX_WIDTH + x];
int counter = 0;
for (uint32_t i : p.intensityAtWavelengths) {
counter += i;
}
if (counter == 0) {
printf(" ");
} else if (counter > 100'000'000) {
printf("%4s", "\u25A0");
} else if (counter > 10'000'000) {
printf("%4s", "\u25A3");
} else if (counter > 1'000'000) {
printf("%4s", "\u25A6");
} else if (counter > 100'000) {
printf("%4s", "\u25A4");
} else {
printf("%4s", "\u25A1");
}
}
std::cout << std::endl;
}
glDeleteBuffers(1, &ssboPixels);
glUseProgram(0);
}
uint32_t TextureTracer::createShadowMap(size_t numPhotons) {
std::vector<Photon> photons = generatePhotons(numPhotons);
uint32_t ssboPhotons;
glGenBuffers(1, &ssboPhotons);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboPhotons);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(Photon) * photons.size(),
photons.data(), GL_DYNAMIC_COPY);
traceThroughTexture(ssboPhotons, photons.size());
glDeleteBuffers(1, &ssboPhotons);
glDeleteProgram(mTextureTracerProgram);
glDisable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(nullptr, nullptr);
return 0;
}
}
main.cpp
#include <GL/glew.h>
#include <GL/glut.h>
#include "TextureTracer.hpp"
int main(int argc, char *argv[]) {
glutInit(&argc, argv);
glutCreateWindow("OpenGL needs a window o.O");
glewInit();
auto mapper = gpu::TextureTracer();
// WITH 100'000 PHOTONS IT WORKS, WITH 1'000'000 PHOTONS NOT WHY?
mapper.createShadowMap(100'000);
return 0;
}
Operating systems cancel GPU program executions if they take too long. On Windows it is generally two seconds and on Linux it is five seconds most of the time, but it can vary.
This is to detect GPU programs that are stuck and cancel them. There are different methods to get around this timeout, but they all require admin/root privileges, which is not always available.
If possible the execution can be split up into multiple invocations like in the following snippet:
const uint32_t passSize = 2048u;
const uint32_t numPasses = (numPhotons / passSize) + 1;
const uint32_t numThreads = 64u;
const uint32_t numBlocks = passSize / numThreads;
glUniform1ui(glGetUniformLocation(mTextureTracerProgram, "passSize"), passSize);
for (uint32_t pass = 0u; pass < numPasses; ++pass) {
glUniform1ui(glGetUniformLocation(mTextureTracerProgram, "pass"), pass);
glDispatchComputeGroupSizeARB(numBlocks, 1, 1, numThreads, 1, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glFlush();
glFinish();
}
The glFlush() and glFinish() calls are important or the executions will get bundled together and the OS triggers a timeout anyways.
In the shader you just need to access the right sections of the input data like so:
// other stuff
uniform uint pass;
uniform uint passSize;
void main() {
uint gid = gl_GlobalInvocationID.x;
uint passId = pass * passSize + gid;
if (passId >= photons.length()) return;
Photon photon = photons[passId];
// rest of program
}
This is all.
If you want to disable the OS timeouts here is a relevant post for Linux: https://stackoverflow.com/a/30520538/5543884
And here is a post regarding Windows: https://stackoverflow.com/a/29759823/5543884
I'm trying to get a grasp of basic features in libpng. To do this, I've used this snippet and adapted to my own example.
int x, y;
png_byte color_type = PNG_COLOR_TYPE_RGBA;
png_byte bit_depth = 16;
png_structp png_ptr;
png_infop info_ptr;
auto create_image(const int height, const int width) {
png_byte **rows = new png_byte *[height];
for (auto i = 0; i < height; i++)
rows[i] = new png_byte[width * 4];
return rows;
}
auto modify_image(const int height, const int width, png_byte **rows) {
for (auto i = 0; i < height; i++) {
for (auto j = 0; j < width; j++) {
// Red channel
rows[i][j * 4 + 0] = (j * 127.) / width;
// Blue channel
rows[i][j * 4 + 2] = (i * 127.) / height;
// Alpha channel
rows[i][j * 4 + 3] = 127;
}
}
}
void write(const std::string& filename, const int height, const int width, png_byte** rows)
{
/* create file */
FILE *fp = fopen(filename.c_str(), "wb");
if (!fp)
abort_("[write_png_file] File %s could not be opened for writing", filename);
/* initialize stuff */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
abort_("[write_png_file] png_create_write_struct failed");
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
abort_("[write_png_file] png_create_info_struct failed");
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[write_png_file] Error during init_io");
png_init_io(png_ptr, fp);
/* write header */
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[write_png_file] Error during writing header");
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
/* write bytes */
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[write_png_file] Error during writing bytes");
png_write_image(png_ptr, rows);
/* end write */
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[write_png_file] Error during end of write");
png_write_end(png_ptr, NULL);
fclose(fp);
}
Though, when I run my code:
void render(const int height, const int width, const std::string &filename) {
png_byte **rows = create_image(height, width);
modify_image(height, width, rows);
write(filename, height, width, rows);
}
The problem is... I don't get the expected result at all. While I was expecting a square image with some kind of gradient I get... two rectangular rectangles.
Also, I've noticed that these two rectangles are stretched: while trying to render a circle, I've found that the circle was distorted and doubling its width makes it an actual circle...
Finally, I've seen that on the second rectangle, the last row seems to be random data (which is not the case on the first rectangle).
Please suggest if you have any ideas.
You create image with 16 bit depth yet use 1 byte per channel. Output image consists of odd / even rows of your original image being put on the same row. Basically each line of the right rectangle is caused by a buffer overrun. You need to allocate buffers that are twice as big, that is width * 4 * 2 and fill higher and lower bytes of each channel separately.
This is my first post, so I am thrilled to get some new insights and enlarge my knowledge. Currently I am working on a C-project where a binary raw file with 3d-data is loaded, processed in CUDA and saved in a new binary raw file.
This is based on the simpleTexture3D project from CUDA Samples:
This is my cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
// includes, cuda
#include <vector_types.h>
#include <driver_functions.h>
#include <cuda_runtime.h>
// CUDA utilities and system includes
#include <helper_cuda.h>
#include <helper_functions.h>
#include <vector_types.h>
typedef unsigned int uint;
typedef unsigned char uchar;
const char *sSDKsample = "simpleTexture3D";
const char *volumeFilename = "Bucky.raw";
const cudaExtent volumeSize = make_cudaExtent(32, 32, 32);
const uint width = 64, height = 64, depth=64;
//const char *volumeFilename = "TestOCT.raw";
//const cudaExtent volumeSize = make_cudaExtent(1024, 512, 512);
//
//const uint width = 1024, height = 512, depth=512;
const dim3 blockSize(8, 8, 8);
const dim3 gridSize(width / blockSize.x, height / blockSize.y, depth / blockSize.z);
uint *d_output = NULL;
int *pArgc = NULL;
char **pArgv = NULL;
extern "C" void cleanup();
extern "C" void initCuda(const uchar *h_volume, cudaExtent volumeSize);
extern "C" void render_kernel(dim3 gridSize, dim3 blockSize, uint *d_output, uint imageW, uint imageH, uint imageD);
void loadVolumeData(char *exec_path);
// render image using CUDA
void render()
{
// call CUDA kernel
render_kernel(gridSize, blockSize, d_output, width, height, depth);
getLastCudaError("render_kernel failed");
}
void cleanup()
{
// cudaDeviceReset causes the driver to clean up all state. While
// not mandatory in normal operation, it is good practice. It is also
// needed to ensure correct operation when the application is being
// profiled. Calling cudaDeviceReset causes all profile data to be
// flushed before the application exits
checkCudaErrors(cudaDeviceReset());
}
// Load raw data from disk
uchar *loadRawFile(const char *filename, size_t size)
{
FILE *fp = fopen(filename, "rb");
if (!fp)
{
fprintf(stderr, "Error opening file '%s'\n", filename);
return 0;
}
uchar *data = (uchar *) malloc(size);
size_t read = fread(data, 1, size, fp);
fclose(fp);
printf("Read '%s', %lu bytes\n", filename, read);
return data;
}
// write raw data to disk
int writeRawFile(const char *filename, uchar *data, size_t size)
{
int returnState=0;
// cut file extension from filename
char *a=strdup(filename); //via strdup you dumb a const char to char, you must free it yourself
int len = strlen(a);
a[len-4] = '\0'; //deletes '.raw'
//printf("%s\n",a);
char b[50];
sprintf(b, "_%dx%dx%d_out.raw", width, height, depth);
//char b[]="_out.raw"; //Add suffix out to filename
char buffer[256]; // <- danger, only storage for 256 characters.
strncpy(buffer, a, sizeof(buffer));
strncat(buffer, b, sizeof(buffer));
free(a);
FILE *fp = fopen(buffer, "wb"); //Open or create file for writing as binary, all existing data is cleared
if (!fp)
{
fprintf(stderr, "Error opening or creating file '%s'\n", buffer);
return 0;
}
size_t write = fwrite(data, 1, size, fp);
fclose(fp);
if (write==size)
{
printf("Wrote %lu bytes to '%s'\n", write, buffer);
return 0;
}
else
{
printf("Error writing data to file '%s'\n", buffer);
return 1;
}
}
// General initialization call for CUDA Device
int chooseCudaDevice(int argc, char **argv)
{
int result = 0;
result = findCudaDevice(argc, (const char **)argv);
return result;
}
void runAutoTest(char *exec_path, char *PathToFile)
{
// set path
char *path;
if (PathToFile == NULL)
{
path = sdkFindFilePath(volumeFilename, exec_path);
}
else
{
path = PathToFile;
}
if (path == NULL)
{
fprintf(stderr, "Error unable to find 3D Volume file: '%s'\n", volumeFilename);
exit(EXIT_FAILURE);
}
// Allocate output memory
checkCudaErrors(cudaMalloc((void **)&d_output, width*height*depth*sizeof(uchar)));
// zero out the output array with cudaMemset
cudaMemset(d_output, 0, width*height*depth*sizeof(uchar));
// render the volumeData
render_kernel(gridSize, blockSize, d_output, width, height, depth);
checkCudaErrors(cudaDeviceSynchronize());
getLastCudaError("render_kernel failed");
uchar *h_output = (uchar*)malloc(width*height*depth);
checkCudaErrors(cudaMemcpy(h_output, d_output, width*height*depth*sizeof(uchar), cudaMemcpyDeviceToHost));
int wState=writeRawFile(path,h_output,width*height*depth);
checkCudaErrors(cudaFree(d_output));
free(h_output);
// cudaDeviceReset causes the driver to clean up all state. While
// not mandatory in normal operation, it is good practice. It is also
// needed to ensure correct operation when the application is being
// profiled. Calling cudaDeviceReset causes all profile data to be
// flushed before the application exits
cudaDeviceReset();
//exit(bTestResult ? EXIT_SUCCESS : EXIT_FAILURE);
}
void loadVolumeData(char *exec_path, char *PathToFile)
{
char *path;
// load volume data
if (PathToFile == NULL)
{
path = sdkFindFilePath(volumeFilename, exec_path);
}
else
{
path = PathToFile;
}
if (path == NULL)
{
fprintf(stderr, "Error unable to find 3D Volume file: '%s'\n", volumeFilename);
exit(EXIT_FAILURE);
}
size_t size = volumeSize.width*volumeSize.height*volumeSize.depth;
uchar *h_volume = loadRawFile(path, size);
//int wState=writeRawFile(path,h_volume,size);
initCuda(h_volume, volumeSize);
free(h_volume);
}
////////////////////////////////////////////////////////////////////////////////
// Program main
////////////////////////////////////////////////////////////////////////////////
int
main(int argc, char **argv)
{
pArgc = &argc;
pArgv = argv;
char *image_file = NULL;
printf("%s Starting...\n\n", sSDKsample);
if (checkCmdLineFlag(argc, (const char **)argv, "file")) //Note cmd line argument is -file "PathToFile/File.raw"
{ // for example -file "C:\ProgramData\NVIDIA Corporation\CUDA Samples\v7.0\2_Graphics\simpleTexture3D_FanBeamCorr\data\TestOCT_Kopie.raw"
getCmdLineArgumentString(argc, (const char **)argv, "file", &image_file);
}
if (image_file)
{
chooseCudaDevice(argc, argv);
loadVolumeData(argv[0],image_file);
runAutoTest(argv[0],image_file);
}
else
{
// use command-line specified CUDA device, otherwise use device with highest Gflops/s
chooseCudaDevice(argc, argv);
loadVolumeData(argv[0],NULL);
runAutoTest(argv[0],NULL);
}
printf("I am finished...\n"
"Can I get some ice cream please\n");
exit(EXIT_SUCCESS);
}
And this is my .cu
#ifndef _SIMPLETEXTURE3D_KERNEL_CU_
#define _SIMPLETEXTURE3D_KERNEL_CU_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <helper_cuda.h>
#include <helper_math.h>
typedef unsigned int uint;
typedef unsigned char uchar;
texture<uchar, 3, cudaReadModeNormalizedFloat> tex; // 3D texture
cudaArray *d_volumeArray = 0;
__global__ void
d_render(uint *d_output, uint imageW, uint imageH, uint imageD)
{
uint x = __umul24(blockIdx.x, blockDim.x) + threadIdx.x;
uint y = __umul24(blockIdx.y, blockDim.y) + threadIdx.y;
uint z = __umul24(blockIdx.z, blockDim.z) + threadIdx.z;
// float u = x / (float) imageW;
// float v = y / (float) imageH;
//float w = z / (float) imageD;
// // read from 3D texture
// float voxel = tex3D(tex, u, v, w);
uint ps=__umul24(imageW,imageH);
if ((x < imageW) && (y < imageH) && (z < imageD))
{
// write output color
uint i = __umul24(z,ps) +__umul24(y, imageW) + x;
d_output[1] = (uchar) 255;//+0*voxel*255;
}
}
extern "C"
void initCuda(const uchar *h_volume, cudaExtent volumeSize)
{
// create 3D array
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<uchar>();
checkCudaErrors(cudaMalloc3DArray(&d_volumeArray, &channelDesc, volumeSize));
// copy data to 3D array
cudaMemcpy3DParms copyParams = {0};
copyParams.srcPtr = make_cudaPitchedPtr((void *)h_volume, volumeSize.width*sizeof(uchar), volumeSize.width, volumeSize.height);
copyParams.dstArray = d_volumeArray;
copyParams.extent = volumeSize;
copyParams.kind = cudaMemcpyHostToDevice;
checkCudaErrors(cudaMemcpy3D(©Params));
// set texture parameters
tex.normalized = true; // access with normalized texture coordinates
tex.filterMode = cudaFilterModeLinear; // linear interpolation
tex.addressMode[0] = cudaAddressModeBorder; // wrap texture coordinates
tex.addressMode[1] = cudaAddressModeBorder;
tex.addressMode[2] = cudaAddressModeBorder;
// bind array to 3D texture
checkCudaErrors(cudaBindTextureToArray(tex, d_volumeArray, channelDesc));
}
extern "C"
void render_kernel(dim3 gridSize, dim3 blockSize, uint *d_output, uint imageW, uint imageH, uint imageD)
{
d_render<<<gridSize, blockSize>>>(d_output, imageW, imageH, imageD);
}
#endif // #ifndef _SIMPLETEXTURE3D_KERNEL_CU_
As you can see, currently, I set all values to zero except the index = 1, which is set to 255. Yet when I now open the image stack in Fiji, I see that the fourth pixel on the first slide is white. If I use index=i instead, I get white vertical lines across the image stack periodically every four columns. Generally spoken, it seems that only every fourth element is beeing indexed in the CudaArray. So I am wondering if there is somekind of error here resulting from sizeof(uchar)=1 and sizeof(uint)=4. There would obviously be the factor 4 :)
I am eager to here from you experts
Cheers Mika
I figured it out by myself. The kernel works with uint* d_output while the copy to the host is written into a uchar* h_output
uchar *h_output = (uchar*)malloc(width*height*depth);
checkCudaErrors(cudaMemcpy(h_output, d_output, width*height*depth*sizeof(uchar), cudaMemcpyDeviceToHost));
This led to this strange behavior
How to convert bmp to png properly? I was using this code:
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_DEPRECATE
#include <png.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
void GetDesktopResolution(int& horizontal, int& vertical)
{
RECT desktop;
// Get a handle to the desktop window
const HWND hDesktop = GetDesktopWindow();
// Get the size of screen to the variable desktop
GetWindowRect(hDesktop, &desktop);
// The top left corner will have coordinates (0,0)
// and the bottom right corner will have coordinates
// (horizontal, vertical)
horizontal = desktop.right;
vertical = desktop.bottom;
}
typedef struct _RGBPixel {
uint8_t blue;
uint8_t green;
uint8_t red;
} RGBPixel;
/* Structure for containing decompressed bitmaps. */
typedef struct _RGBBitmap {
RGBPixel *pixels;
size_t width;
size_t height;
size_t bytewidth;
uint8_t bytes_per_pixel;
} RGBBitmap;
/* Returns pixel of bitmap at given point. */
#define RGBPixelAtPoint(image, x, y) \
*(((image)->pixels) + (((image)->bytewidth * (y)) \
+ ((x) * (image)->bytes_per_pixel)))
/* Attempts to save PNG to file; returns 0 on success, non-zero on error. */
int save_png_to_file(RGBBitmap *bitmap, const char *path)
{
FILE *fp = fopen(path, "wb");
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
size_t x, y;
png_uint_32 bytes_per_row;
png_byte **row_pointers = NULL;
if (fp == NULL) return -1;
/* Initialize the write struct. */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(fp);
return -1;
}
/* Initialize the info struct. */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
png_destroy_write_struct(&png_ptr, NULL);
fclose(fp);
return -1;
}
/* Set up error handling. */
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
return -1;
}
/* Set image attributes. */
png_set_IHDR(png_ptr,
info_ptr,
bitmap->width,
bitmap->height,
8,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
/* Initialize rows of PNG. */
bytes_per_row = bitmap->width * bitmap->bytes_per_pixel;
png_malloc(png_ptr, bitmap->height * sizeof(png_byte *));
for (y = 0; y < bitmap->height; ++y) {
uint8_t *row = (uint8_t *)png_malloc(png_ptr, sizeof(uint8_t)* bitmap->bytes_per_pixel);
row_pointers[y] = (png_byte *)row; /************* MARKED LINE ***************/
for (x = 0; x < bitmap->width; ++x) {
RGBPixel color = RGBPixelAtPoint(bitmap, x, y);
*row++ = color.red;
*row++ = color.green;
*row++ = color.blue;
}
}
/* Actually write the image data. */
png_init_io(png_ptr, fp);
png_set_rows(png_ptr, info_ptr, row_pointers);
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
/* Cleanup. */
for (y = 0; y < bitmap->height; y++) {
png_free(png_ptr, row_pointers[y]);
}
png_free(png_ptr, row_pointers);
/* Finish writing. */
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
return 0;
}
int main()
{
RGBBitmap rgbbitmap;
int w, h;
GetDesktopResolution(w, h);
rgbbitmap.height = h;
rgbbitmap.width = w;
rgbbitmap.bytes_per_pixel = 1;
rgbbitmap.bytewidth = w / 100;
RGBPixel rgbpixel;
rgbpixel.blue = 100;
rgbpixel.green = 100;
rgbpixel.red = 100;
rgbbitmap.pixels = &rgbpixel;
save_png_to_file(&rgbbitmap, "abc.bmp");
return 0;
}
But at runtime, I'm getting this error on the marked line :
Unhandled exception at 0x01258F04 in ProjectName.exe: 0xC0000005: Access violation writing location 0x00000000.
I've searched in the net, I've found this interesting small code, but I wasn't able to found its mother lib. And, I think that using libpng gives you more control on your output image.
How do I fix this issue? Or, maybe it's a wrong way to convert? If so, how may I do that?
You never allocate memory for row_pointers, so it's NULL when you do row_pointers[y] = (png_byte *)row;.
I have use libpng to generate png file. for RGB png, there is no problem. but I want to give the png a transparent property.
I reference some code, and add the places where use * to flaged
*row++ = 230;
*PNG_COLOR_TYPE_RGBA,*
to make it have transparent property,
the code is run sucessfully, but I did't see the transparent effect.
can anyone familiar with libpng or png operation help me?
In this code,
where the pixel_t,& bitmap_t is some data with rgb data.
/* A colored pixel. */
typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} pixel_t;
/* A picture. */
typedef struct {
pixel_t *pixels;
size_t width;
size_t height;
} bitmap_t;
static int save_png_to_file (bitmap_t *bitmap, const char *path)
{
FILE * fp;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
size_t x, y;
png_byte ** row_pointers = NULL;
/* "status" contains the return value of this function. At first
it is set to a value which means 'failure'. When the routine
has finished its work, it is set to a value which means
'success'. */
int status = -1;
/* The following number is set by trial and error only. I cannot
see where it it is documented in the libpng manual.
*/
int pixel_size = 3;
int depth = 8;
fp = fopen (path, "wb");
if (! fp) {
goto fopen_failed;
}
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
goto png_create_write_struct_failed;
}
info_ptr = png_create_info_struct (png_ptr);
if (info_ptr == NULL) {
goto png_create_info_struct_failed;
}
// png_set_invert_alpha(png_ptr);
/* Set up error handling. */
if (setjmp (png_jmpbuf (png_ptr))) {
goto png_failure;
}
/* Set image attributes. */
png_set_IHDR (png_ptr,
info_ptr,
bitmap->width,
bitmap->height,
depth,
*PNG_COLOR_TYPE_RGBA,*
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
/* Initialize rows of PNG. */
//(png_byte **) added by li
row_pointers = (png_byte **)png_malloc (png_ptr, bitmap->height * sizeof (png_byte *));
for (y = 0; y < bitmap->height; ++y) {
//png_byte * added by li
// png_byte *row = (png_byte *)
// png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size);
png_byte *row = (png_byte *)
png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * 4);
row_pointers[y] = row;
for (x = 0; x < bitmap->width; ++x) {
pixel_t * pixel = pixel_at (bitmap, x, y);
*row++ = pixel->red;
*row++ = pixel->green;
*row++ = pixel->blue;
**row++ = 230;*
}
}
/* Write the image data to "fp". */
png_init_io (png_ptr, fp);
png_set_rows (png_ptr, info_ptr, row_pointers);
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
/* The routine has successfully written the file, so we set
"status" to a value which indicates success. */
status = 0;
for (y = 0; y < bitmap->height; y++) {
png_free (png_ptr, row_pointers[y]);
}
png_free (png_ptr, row_pointers);
png_failure:
png_create_info_struct_failed:
png_destroy_write_struct (&png_ptr, &info_ptr);
png_create_write_struct_failed:
fclose (fp);
fopen_failed:
return status;
}
A truecolor PNG image with alpha , with bitdepth=8, stores the transparency as an extra channel, in RGBA order, in the range 0-255 (0=fully transparent; 255=fully opaque).
What you are doing looks correct to me. Only that a value of 230 means "almost opaque", it might be difficult to detect visually the trasnparency. Try with other values.
BTW, bear in mind that there are other ways to add transparency to a PNG image, see my answer here.
Since this question seems to be the top answer for 'libpng add transparency', I'm adding a compile ready source to this answer
// gcc -g -Wall -o png2 png2.c -lpng
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#define uint8_t unsigned char
typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} pixel_t;
typedef struct {
pixel_t *pixels;
size_t width;
size_t height;
} bitmap_t;
pixel_t *pixel_at(bitmap_t *bitmap, int x, int y)
{return &bitmap->pixels[(bitmap->width * y) + x];}
static int save_png_to_file (bitmap_t *bitmap, const char *path)
{
FILE * fp;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
size_t x, y;
png_byte ** row_pointers = NULL;
pixel_t *pixel;
int status = -1;
int depth = 8;
if((fp = fopen(path, "wb")) == NULL)
goto fopen_failed;
if((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL)
goto png_create_write_struct_failed;
if((info_ptr = png_create_info_struct (png_ptr)) == NULL)
goto png_create_info_struct_failed;
if(setjmp (png_jmpbuf (png_ptr)))
goto png_failure;
png_set_IHDR (png_ptr,
info_ptr,
bitmap->width,
bitmap->height,
depth,
PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
row_pointers = (png_byte **)png_malloc (png_ptr, bitmap->height * sizeof (png_byte *));
for(y = 0; y < bitmap->height; y++)
{
png_byte *row = (png_byte *)
png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * 4);
row_pointers[y] = row;
for (x = 0; x < bitmap->width; ++x)
{
pixel = pixel_at (bitmap, x, y);
*row++ = pixel->red;
*row++ = pixel->green;
*row++ = pixel->blue;
*row++ = x; // 0 - completely transparent 255 - completely opaque
}
}
png_init_io (png_ptr, fp);
png_set_rows (png_ptr, info_ptr, row_pointers);
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
status = 0;
for(y = 0; y < bitmap->height; y++)
png_free (png_ptr, row_pointers[y]);
png_free (png_ptr, row_pointers);
png_failure:
png_create_info_struct_failed:
png_destroy_write_struct (&png_ptr, &info_ptr);
png_create_write_struct_failed:
fclose (fp);
fopen_failed:
return status;
}
int main(void)
{
bitmap_t pic;
unsigned char pixels[255*255*3];
int t;
pic.width = 255;
pic.height = 255;
for(t=0; t<255 * 255 * 3; t=t+3) // Just a red square
{
pixels[t+0] = 255;
pixels[t+1] = 0;
pixels[t+2] = 0;
}
pic.pixels = (pixel_t *) &pixels;
save_png_to_file(&pic, "test.png");
return 0;
}