Refactor Image class and image capture
* use GL_MESA_pack_invert to flip captured buffer * move enum PixelFormat to own file * use PixelFormat instead of GL formats in Image * provide captureImage() in CelestiaCore * use CelestiaCore methods to capture images in Qt UIpull/1094/head
parent
c7018259a6
commit
bece12f6e4
|
@ -340,7 +340,7 @@ void Galaxy::renderGalaxyPointSprites(const Vector3f& offset,
|
|||
|
||||
if (galaxyTex == nullptr)
|
||||
{
|
||||
galaxyTex = CreateProceduralTexture(width, height, GL_RGBA,
|
||||
galaxyTex = CreateProceduralTexture(width, height, PixelFormat::RGBA,
|
||||
GalaxyTextureEval);
|
||||
}
|
||||
assert(galaxyTex != nullptr);
|
||||
|
@ -349,7 +349,7 @@ void Galaxy::renderGalaxyPointSprites(const Vector3f& offset,
|
|||
|
||||
if (colorTex == nullptr)
|
||||
{
|
||||
colorTex = CreateProceduralTexture(256, 1, GL_RGBA,
|
||||
colorTex = CreateProceduralTexture(256, 1, PixelFormat::RGBA,
|
||||
ColorTextureEval,
|
||||
Texture::EdgeClamp,
|
||||
Texture::NoMipMaps);
|
||||
|
|
|
@ -430,14 +430,16 @@ void Globular::renderGlobularPointSprites(
|
|||
|
||||
if(centerTex[ic] == nullptr)
|
||||
{
|
||||
centerTex[ic] = CreateProceduralTexture(cntrTexWidth, cntrTexHeight, GL_RGBA,
|
||||
centerTex[ic] = CreateProceduralTexture(cntrTexWidth, cntrTexHeight,
|
||||
PixelFormat::RGBA,
|
||||
CenterCloudTexEval);
|
||||
}
|
||||
assert(centerTex[ic] != nullptr);
|
||||
|
||||
if (globularTex == nullptr)
|
||||
{
|
||||
globularTex = CreateProceduralTexture(starTexWidth, starTexHeight, GL_RGBA,
|
||||
globularTex = CreateProceduralTexture(starTexWidth, starTexHeight,
|
||||
PixelFormat::RGBA,
|
||||
GlobularTextureEval);
|
||||
}
|
||||
assert(globularTex != nullptr);
|
||||
|
|
|
@ -15,6 +15,7 @@ bool EXT_framebuffer_object = false;
|
|||
bool ARB_shader_texture_lod = false;
|
||||
bool EXT_texture_compression_s3tc = false;
|
||||
bool EXT_texture_filter_anisotropic = false;
|
||||
bool MESA_pack_invert = false;
|
||||
GLint maxPointSize = 0;
|
||||
GLfloat maxLineWidth = 0.0f;
|
||||
|
||||
|
@ -42,6 +43,7 @@ bool init(util::array_view<std::string> ignore) noexcept
|
|||
ARB_shader_texture_lod = check_extension(ignore, "GL_ARB_shader_texture_lod");
|
||||
EXT_texture_compression_s3tc = check_extension(ignore, "GL_EXT_texture_compression_s3tc");
|
||||
EXT_texture_filter_anisotropic = check_extension(ignore, "GL_EXT_texture_filter_anisotropic");
|
||||
MESA_pack_invert = check_extension(ignore, "GL_MESA_pack_invert");
|
||||
|
||||
GLint pointSizeRange[2];
|
||||
GLfloat lineWidthRange[2];
|
||||
|
|
|
@ -40,6 +40,7 @@ constexpr const int GLES_2 = 20;
|
|||
extern bool ARB_shader_texture_lod;
|
||||
extern bool EXT_texture_compression_s3tc;
|
||||
extern bool EXT_texture_filter_anisotropic;
|
||||
extern bool MESA_pack_invert;
|
||||
#ifdef GL_ES
|
||||
extern bool OES_vertex_array_object;
|
||||
#else
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "image.h"
|
||||
|
||||
using namespace std;
|
||||
using celestia::PixelFormat;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -28,32 +29,27 @@ int pad(int n)
|
|||
return (n + 3) & ~0x3;
|
||||
}
|
||||
|
||||
int formatComponents(int fmt)
|
||||
int formatComponents(PixelFormat fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case GL_RGBA:
|
||||
case GL_BGRA_EXT:
|
||||
case PixelFormat::RGBA:
|
||||
case PixelFormat::BGRA:
|
||||
return 4;
|
||||
case GL_RGB:
|
||||
#ifndef GL_ES
|
||||
case GL_BGR_EXT:
|
||||
#endif
|
||||
case PixelFormat::RGB:
|
||||
case PixelFormat::BGR:
|
||||
return 3;
|
||||
case GL_LUMINANCE_ALPHA:
|
||||
#ifndef GL_ES
|
||||
case GL_DSDT_NV:
|
||||
#endif
|
||||
case PixelFormat::LUM_ALPHA:
|
||||
return 2;
|
||||
case GL_ALPHA:
|
||||
case GL_LUMINANCE:
|
||||
case PixelFormat::ALPHA:
|
||||
case PixelFormat::LUMINANCE:
|
||||
return 1;
|
||||
|
||||
// Compressed formats
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
case PixelFormat::DXT1:
|
||||
return 3;
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
case PixelFormat::DXT3:
|
||||
case PixelFormat::DXT5:
|
||||
return 4;
|
||||
|
||||
// Unknown format
|
||||
|
@ -62,18 +58,18 @@ int formatComponents(int fmt)
|
|||
}
|
||||
}
|
||||
|
||||
int calcMipLevelSize(int fmt, int w, int h, int mip)
|
||||
int calcMipLevelSize(PixelFormat fmt, int w, int h, int mip)
|
||||
{
|
||||
w = max(w >> mip, 1);
|
||||
h = max(h >> mip, 1);
|
||||
|
||||
switch (fmt)
|
||||
{
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
case PixelFormat::DXT1:
|
||||
// 4x4 blocks, 8 bytes per block
|
||||
return ((w + 3) / 4) * ((h + 3) / 4) * 8;
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
case PixelFormat::DXT3:
|
||||
case PixelFormat::DXT5:
|
||||
// 4x4 blocks, 16 bytes per block
|
||||
return ((w + 3) / 4) * ((h + 3) / 4) * 16;
|
||||
default:
|
||||
|
@ -82,7 +78,7 @@ int calcMipLevelSize(int fmt, int w, int h, int mip)
|
|||
}
|
||||
} // anonymous namespace
|
||||
|
||||
Image::Image(int fmt, int w, int h, int mip) :
|
||||
Image::Image(PixelFormat fmt, int w, int h, int mip) :
|
||||
width(w),
|
||||
height(h),
|
||||
mipLevels(mip),
|
||||
|
@ -96,12 +92,12 @@ Image::Image(int fmt, int w, int h, int mip) :
|
|||
size = 1;
|
||||
for (int i = 0; i < mipLevels; i++)
|
||||
size += calcMipLevelSize(fmt, w, h, i);
|
||||
pixels = new unsigned char[size];
|
||||
pixels = make_unique<uint8_t[]>(size);
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
bool Image::isValid() const noexcept
|
||||
{
|
||||
delete[] pixels;
|
||||
return pixels != nullptr;
|
||||
}
|
||||
|
||||
int Image::getWidth() const
|
||||
|
@ -129,7 +125,7 @@ int Image::getSize() const
|
|||
return size;
|
||||
}
|
||||
|
||||
int Image::getFormat() const
|
||||
PixelFormat Image::getFormat() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
@ -139,12 +135,12 @@ int Image::getComponents() const
|
|||
return components;
|
||||
}
|
||||
|
||||
unsigned char* Image::getPixels()
|
||||
uint8_t* Image::getPixels()
|
||||
{
|
||||
return pixels;
|
||||
return pixels.get();
|
||||
}
|
||||
|
||||
unsigned char* Image::getPixelRow(int mip, int row)
|
||||
uint8_t* Image::getPixelRow(int mip, int row)
|
||||
{
|
||||
/*int w = max(width >> mip, 1); Unused*/
|
||||
int h = max(height >> mip, 1);
|
||||
|
@ -158,12 +154,12 @@ unsigned char* Image::getPixelRow(int mip, int row)
|
|||
return getMipLevel(mip) + row * pitch;
|
||||
}
|
||||
|
||||
unsigned char* Image::getPixelRow(int row)
|
||||
uint8_t* Image::getPixelRow(int row)
|
||||
{
|
||||
return getPixelRow(0, row);
|
||||
}
|
||||
|
||||
unsigned char* Image::getMipLevel(int mip)
|
||||
uint8_t* Image::getMipLevel(int mip)
|
||||
{
|
||||
if (mip >= mipLevels)
|
||||
return nullptr;
|
||||
|
@ -172,7 +168,7 @@ unsigned char* Image::getMipLevel(int mip)
|
|||
for (int i = 0; i < mip; i++)
|
||||
offset += calcMipLevelSize(format, width, height, i);
|
||||
|
||||
return pixels + offset;
|
||||
return pixels.get() + offset;
|
||||
}
|
||||
|
||||
int Image::getMipLevelSize(int mip) const
|
||||
|
@ -187,9 +183,9 @@ bool Image::isCompressed() const
|
|||
{
|
||||
switch (format)
|
||||
{
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
case PixelFormat::DXT1:
|
||||
case PixelFormat::DXT3:
|
||||
case PixelFormat::DXT5:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -200,12 +196,12 @@ bool Image::hasAlpha() const
|
|||
{
|
||||
switch (format)
|
||||
{
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
case GL_RGBA:
|
||||
case GL_BGRA_EXT:
|
||||
case GL_LUMINANCE_ALPHA:
|
||||
case GL_ALPHA:
|
||||
case PixelFormat::DXT3:
|
||||
case PixelFormat::DXT5:
|
||||
case PixelFormat::RGBA:
|
||||
case PixelFormat::BGRA:
|
||||
case PixelFormat::LUM_ALPHA:
|
||||
case PixelFormat::ALPHA:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -225,9 +221,9 @@ Image* Image::computeNormalMap(float scale, bool wrap) const
|
|||
if (isCompressed())
|
||||
return nullptr;
|
||||
|
||||
auto* normalMap = new Image(GL_RGBA, width, height);
|
||||
auto* normalMap = new Image(PixelFormat::RGBA, width, height);
|
||||
|
||||
unsigned char* nmPixels = normalMap->getPixels();
|
||||
uint8_t* nmPixels = normalMap->getPixels();
|
||||
int nmPitch = normalMap->getPitch();
|
||||
|
||||
// Compute normals using differences between adjacent texels.
|
||||
|
@ -275,9 +271,9 @@ Image* Image::computeNormalMap(float scale, bool wrap) const
|
|||
float rmag = 1.0f / mag;
|
||||
|
||||
int n = i * nmPitch + j * 4;
|
||||
nmPixels[n] = (unsigned char) (128 + 127 * dx * rmag);
|
||||
nmPixels[n + 1] = (unsigned char) (128 + 127 * dy * rmag);
|
||||
nmPixels[n + 2] = (unsigned char) (128 + 127 * rmag);
|
||||
nmPixels[n] = (uint8_t) (128 + 127 * dx * rmag);
|
||||
nmPixels[n + 1] = (uint8_t) (128 + 127 * dy * rmag);
|
||||
nmPixels[n + 2] = (uint8_t) (128 + 127 * rmag);
|
||||
nmPixels[n + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <celcompat/filesystem.h>
|
||||
#include <celengine/pixelformat.h>
|
||||
|
||||
// The image class supports multiple GL formats, including compressed ones.
|
||||
// Mipmaps may be stored within an image as well. The mipmaps are stored in
|
||||
|
@ -21,19 +23,24 @@
|
|||
class Image
|
||||
{
|
||||
public:
|
||||
Image(int fmt, int w, int h, int mip = 1);
|
||||
~Image();
|
||||
Image(celestia::PixelFormat format, int w, int h, int mip = 1);
|
||||
~Image() = default;
|
||||
Image(Image&&) = default;
|
||||
Image(const Image&) = delete;
|
||||
Image& operator=(Image&&) = default;
|
||||
Image& operator=(const Image&) = delete;
|
||||
|
||||
bool isValid() const noexcept;
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
int getPitch() const;
|
||||
int getMipLevelCount() const;
|
||||
int getFormat() const;
|
||||
celestia::PixelFormat getFormat() const;
|
||||
int getComponents() const;
|
||||
unsigned char* getPixels();
|
||||
unsigned char* getPixelRow(int row);
|
||||
unsigned char* getPixelRow(int mip, int row);
|
||||
unsigned char* getMipLevel(int mip);
|
||||
uint8_t* getPixels();
|
||||
uint8_t* getPixelRow(int row);
|
||||
uint8_t* getPixelRow(int mip, int row);
|
||||
uint8_t* getMipLevel(int mip);
|
||||
int getSize() const;
|
||||
int getMipLevelSize(int mip) const;
|
||||
|
||||
|
@ -54,9 +61,9 @@ class Image
|
|||
int pitch;
|
||||
int mipLevels;
|
||||
int components;
|
||||
int format;
|
||||
celestia::PixelFormat format;
|
||||
int size;
|
||||
unsigned char* pixels{ nullptr };
|
||||
std::unique_ptr<uint8_t[]> pixels;
|
||||
};
|
||||
|
||||
Image* LoadImageFromFile(const fs::path& filename);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// pixelformat.h
|
||||
//
|
||||
// Copyright (C) 2021-present, the Celestia Development Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace celestia
|
||||
{
|
||||
enum class PixelFormat
|
||||
{
|
||||
INVALID = -1,
|
||||
RGB = 0x1907, // GL_RGB
|
||||
RGBA = 0x1908, // GL_RGBA
|
||||
BGR = 0x80E0, // GL_BGR
|
||||
BGRA = 0x80E1, // GL_BGRA
|
||||
LUM_ALPHA = 0x190A, // GL_LUMINANCE_ALPHA
|
||||
ALPHA = 0x1906, // GL_ALPHA
|
||||
LUMINANCE = 0x1909, // GL_LUMINANCE
|
||||
DXT1 = 0x83F1, // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
|
||||
DXT3 = 0x83F2, // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
|
||||
DXT5 = 0x83F3, // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
|
||||
};
|
||||
}
|
|
@ -33,7 +33,6 @@ std::ofstream hdrlog;
|
|||
#define EXPOSURE_HALFLIFE 0.4f
|
||||
#endif
|
||||
|
||||
#include <config.h>
|
||||
#include "render.h"
|
||||
#include "boundaries.h"
|
||||
#include "dsorenderer.h"
|
||||
|
@ -85,6 +84,10 @@ std::ofstream hdrlog;
|
|||
#ifdef USE_GLCONTEXT
|
||||
#include "glcontext.h"
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
#include <malloc.h>
|
||||
#define alloca(s) _alloca(s)
|
||||
#endif
|
||||
|
||||
using namespace cmod;
|
||||
using namespace Eigen;
|
||||
|
@ -462,7 +465,7 @@ static void BuildGlareMipLevel2(unsigned char* mipPixels,
|
|||
static Texture* BuildGaussianDiscTexture(unsigned int log2size)
|
||||
{
|
||||
unsigned int size = 1 << log2size;
|
||||
Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
|
||||
Image* img = new Image(PixelFormat::LUMINANCE, size, size, log2size + 1);
|
||||
|
||||
for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
|
||||
{
|
||||
|
@ -486,7 +489,7 @@ static Texture* BuildGaussianDiscTexture(unsigned int log2size)
|
|||
static Texture* BuildGaussianGlareTexture(unsigned int log2size)
|
||||
{
|
||||
unsigned int size = 1 << log2size;
|
||||
Image* img = new Image(GL_LUMINANCE, size, size, log2size + 1);
|
||||
Image* img = new Image(PixelFormat::LUMINANCE, size, size, log2size + 1);
|
||||
|
||||
for (unsigned int mipLevel = 0; mipLevel <= log2size; mipLevel++)
|
||||
{
|
||||
|
@ -600,7 +603,7 @@ bool Renderer::init(
|
|||
}
|
||||
|
||||
#ifdef USE_HDR
|
||||
Image *testImg = new Image(GL_LUMINANCE_ALPHA, 1, 1);
|
||||
Image *testImg = new Image(PixelFormat::LUM_ALPHA, 1, 1);
|
||||
ImageTexture *testTex = new ImageTexture(*testImg,
|
||||
Texture::EdgeClamp,
|
||||
Texture::NoMipMaps);
|
||||
|
@ -635,6 +638,9 @@ bool Renderer::init(
|
|||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
if (gl::MESA_pack_invert)
|
||||
glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE);
|
||||
|
||||
// LEQUAL rather than LESS required for multipass rendering
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
|
@ -5493,19 +5499,54 @@ void Renderer::disableDepthTest() noexcept
|
|||
}
|
||||
}
|
||||
|
||||
constexpr GLenum toGLFormat(Renderer::PixelFormat format)
|
||||
constexpr GLenum toGLFormat(PixelFormat format)
|
||||
{
|
||||
return (GLenum) format;
|
||||
}
|
||||
|
||||
bool Renderer::captureFrame(int x, int y, int w, int h, Renderer::PixelFormat format, unsigned char* buffer, bool back) const
|
||||
constexpr int formatWidth(PixelFormat format)
|
||||
{
|
||||
return format == PixelFormat::RGB
|
||||
#ifndef GL_ES
|
||||
glReadBuffer(back ? GL_BACK : GL_FRONT);
|
||||
|| format == PixelFormat::BGR
|
||||
#endif
|
||||
glReadPixels(x, y, w, h, toGLFormat(format), GL_UNSIGNED_BYTE, (void*) buffer);
|
||||
? 3 : 4;
|
||||
}
|
||||
|
||||
return glGetError() == GL_NO_ERROR;
|
||||
PixelFormat
|
||||
Renderer::getPreferredCaptureFormat() const noexcept
|
||||
{
|
||||
#ifdef GL_ES
|
||||
return PixelFormat::RGBA;
|
||||
#else
|
||||
return PixelFormat::RGB;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Renderer::captureFrame(int x, int y, int w, int h, PixelFormat format, unsigned char* buffer) const
|
||||
{
|
||||
glReadPixels(x, y, w, h, toGLFormat(format), GL_UNSIGNED_BYTE, (void*) buffer);
|
||||
bool ok = glGetError() == GL_NO_ERROR;
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
if (!gl::MESA_pack_invert)
|
||||
{
|
||||
int realWidth = w * formatWidth(format);
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
uint8_t tempLine[realWidth]; // G++ supports VLA as an extension
|
||||
#else
|
||||
uint8_t *tempLine = static_cast<uint8_t*>(alloca(realWidth));
|
||||
#endif
|
||||
uint8_t *fb = buffer;
|
||||
for (int i = 0, p = realWidth * (h - 1); i < p; i += realWidth, p -= realWidth)
|
||||
{
|
||||
memcpy(tempLine, &fb[i], realWidth);
|
||||
memcpy(&fb[i], &fb[p], realWidth);
|
||||
memcpy(&fb[p], tempLine, realWidth);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void Renderer::drawRectangle(const celestia::Rect &r, int fishEyeOverrideMode, const Eigen::Matrix4f& p, const Eigen::Matrix4f& m)
|
||||
|
|
|
@ -252,17 +252,6 @@ class Renderer
|
|||
StarStyleCount = 3,
|
||||
};
|
||||
|
||||
// Pixel formats for image and video capture.
|
||||
// Currently we map them 1:1 to GL
|
||||
enum class PixelFormat
|
||||
{
|
||||
RGBA = GL_RGBA,
|
||||
RGB = GL_RGB,
|
||||
#ifndef GL_ES
|
||||
BGR_EXT = GL_BGR_EXT
|
||||
#endif
|
||||
};
|
||||
|
||||
uint64_t getRenderFlags() const;
|
||||
void setRenderFlags(uint64_t);
|
||||
int getLabelMode() const;
|
||||
|
@ -314,6 +303,8 @@ class Renderer
|
|||
void enableDepthTest() noexcept;
|
||||
void disableDepthTest() noexcept;
|
||||
|
||||
celestia::PixelFormat getPreferredCaptureFormat() const noexcept;
|
||||
|
||||
void drawRectangle(const celestia::Rect& r, int fishEyeOverrideMode, const Eigen::Matrix4f& p, const Eigen::Matrix4f& m = Eigen::Matrix4f::Identity());
|
||||
void setRenderRegion(int x, int y, int width, int height, bool withScissor = true);
|
||||
|
||||
|
@ -324,7 +315,7 @@ class Renderer
|
|||
void setSolarSystemMaxDistance(float);
|
||||
void setShadowMapSize(unsigned);
|
||||
|
||||
bool captureFrame(int, int, int, int, PixelFormat format, unsigned char*, bool = false) const;
|
||||
bool captureFrame(int, int, int, int, celestia::PixelFormat format, unsigned char*) const;
|
||||
|
||||
void renderMarker(celestia::MarkerRepresentation::Symbol symbol,
|
||||
float size,
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#include <config.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
@ -117,40 +116,37 @@ static const TextureCaps& GetTextureCaps()
|
|||
}
|
||||
|
||||
|
||||
|
||||
static int getInternalFormat(int format)
|
||||
static int getInternalFormat(PixelFormat format)
|
||||
{
|
||||
#ifdef GL_ES
|
||||
switch (format)
|
||||
{
|
||||
case GL_RGBA:
|
||||
case GL_RGB:
|
||||
case GL_LUMINANCE_ALPHA:
|
||||
case GL_ALPHA:
|
||||
case GL_LUMINANCE:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
return format;
|
||||
case PixelFormat::RGBA:
|
||||
case PixelFormat::RGB:
|
||||
case PixelFormat::LUM_ALPHA:
|
||||
case PixelFormat::ALPHA:
|
||||
case PixelFormat::LUMINANCE:
|
||||
case PixelFormat::DXT1:
|
||||
case PixelFormat::DXT3:
|
||||
case PixelFormat::DXT5:
|
||||
return (int) format;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
switch (format)
|
||||
{
|
||||
case GL_RGBA:
|
||||
case GL_BGRA:
|
||||
case GL_RGB:
|
||||
case GL_BGR:
|
||||
case GL_LUMINANCE_ALPHA:
|
||||
case GL_ALPHA:
|
||||
case GL_INTENSITY:
|
||||
case GL_LUMINANCE:
|
||||
case GL_DSDT_NV:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
return format;
|
||||
case PixelFormat::RGBA:
|
||||
case PixelFormat::BGRA:
|
||||
case PixelFormat::RGB:
|
||||
case PixelFormat::BGR:
|
||||
case PixelFormat::LUM_ALPHA:
|
||||
case PixelFormat::ALPHA:
|
||||
case PixelFormat::LUMINANCE:
|
||||
case PixelFormat::DXT1:
|
||||
case PixelFormat::DXT3:
|
||||
case PixelFormat::DXT5:
|
||||
return (int) format;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -190,9 +186,9 @@ static int getCompressedInternalFormat(int format)
|
|||
#endif
|
||||
|
||||
|
||||
static int getCompressedBlockSize(int format)
|
||||
static int getCompressedBlockSize(PixelFormat format)
|
||||
{
|
||||
return format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16;
|
||||
return format == PixelFormat::DXT1 ? 8 : 16;
|
||||
}
|
||||
|
||||
|
||||
|
@ -738,14 +734,13 @@ const TextureTile TiledTexture::getTile(int lod, int u, int v)
|
|||
}
|
||||
|
||||
|
||||
|
||||
CubeMap::CubeMap(Image* faces[]) :
|
||||
Texture(faces[0]->getWidth(), faces[0]->getHeight()),
|
||||
glName(0)
|
||||
{
|
||||
// Verify that all the faces are square and have the same size
|
||||
int width = faces[0]->getWidth();
|
||||
int format = faces[0]->getFormat();
|
||||
PixelFormat format = faces[0]->getFormat();
|
||||
int i = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
|
@ -855,7 +850,7 @@ void CubeMap::setBorderColor(Color borderColor)
|
|||
|
||||
|
||||
Texture* CreateProceduralTexture(int width, int height,
|
||||
int format,
|
||||
PixelFormat format,
|
||||
ProceduralTexEval func,
|
||||
Texture::AddressMode addressMode,
|
||||
Texture::MipMapMode mipMode)
|
||||
|
@ -880,7 +875,7 @@ Texture* CreateProceduralTexture(int width, int height,
|
|||
|
||||
|
||||
Texture* CreateProceduralTexture(int width, int height,
|
||||
int format,
|
||||
PixelFormat format,
|
||||
TexelFunctionObject& func,
|
||||
Texture::AddressMode addressMode,
|
||||
Texture::MipMapMode mipMode)
|
||||
|
@ -940,7 +935,8 @@ static Vector3f cubeVector(int face, float s, float t)
|
|||
}
|
||||
|
||||
|
||||
extern Texture* CreateProceduralCubeMap(int size, int format,
|
||||
Texture* CreateProceduralCubeMap(int size,
|
||||
PixelFormat format,
|
||||
ProceduralTexEval func)
|
||||
{
|
||||
Image* faces[6];
|
||||
|
@ -1056,7 +1052,7 @@ Texture* LoadTextureFromFile(const fs::path& filename,
|
|||
// compressed normal map. There's no separate OpenGL format for dxt5
|
||||
// normal maps, so the file extension is the only thing that
|
||||
// distinguishes it from a plain old dxt5 texture.
|
||||
if (img->getFormat() == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
|
||||
if (img->getFormat() == PixelFormat::DXT5)
|
||||
{
|
||||
tex->setFormatOptions(Texture::DXT5NormalMap);
|
||||
}
|
||||
|
|
|
@ -168,16 +168,16 @@ class CubeMap : public Texture
|
|||
|
||||
|
||||
extern Texture* CreateProceduralTexture(int width, int height,
|
||||
int format,
|
||||
celestia::PixelFormat format,
|
||||
ProceduralTexEval func,
|
||||
Texture::AddressMode addressMode = Texture::EdgeClamp,
|
||||
Texture::MipMapMode mipMode = Texture::DefaultMipMaps);
|
||||
extern Texture* CreateProceduralTexture(int width, int height,
|
||||
int format,
|
||||
celestia::PixelFormat format,
|
||||
TexelFunctionObject& func,
|
||||
Texture::AddressMode addressMode = Texture::EdgeClamp,
|
||||
Texture::MipMapMode mipMode = Texture::DefaultMipMaps);
|
||||
extern Texture* CreateProceduralCubeMap(int size, int format,
|
||||
extern Texture* CreateProceduralCubeMap(int size, celestia::PixelFormat format,
|
||||
ProceduralTexEval func);
|
||||
|
||||
extern Texture* LoadTextureFromFile(const fs::path& filename,
|
||||
|
|
|
@ -13,8 +13,6 @@ set(CELESTIA_SOURCES
|
|||
favorites.h
|
||||
helper.cpp
|
||||
helper.h
|
||||
imagecapture.cpp
|
||||
imagecapture.h
|
||||
scriptmenu.cpp
|
||||
scriptmenu.h
|
||||
url.cpp
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <celengine/planetgrid.h>
|
||||
#include <celengine/visibleregion.h>
|
||||
#include <celengine/framebuffer.h>
|
||||
#include <celimage/imageformats.h>
|
||||
#include <celmath/geomutil.h>
|
||||
#include <celutil/color.h>
|
||||
#include <celutil/filetype.h>
|
||||
|
@ -60,12 +61,11 @@
|
|||
#endif
|
||||
#include <celttf/truetypefont.h>
|
||||
|
||||
#include "imagecapture.h"
|
||||
|
||||
using namespace Eigen;
|
||||
using namespace std;
|
||||
using namespace astro::literals;
|
||||
using namespace celmath;
|
||||
using namespace celestia;
|
||||
using namespace celestia::scripts;
|
||||
using namespace celestia::util;
|
||||
|
||||
|
@ -4719,20 +4719,49 @@ View* CelestiaCore::getViewByObserver(const Observer *obs) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Image CelestiaCore::captureImage() const
|
||||
{
|
||||
// Get the dimensions of the current viewport
|
||||
array<int, 4> viewport;
|
||||
getRenderer()->getViewport(viewport);
|
||||
|
||||
PixelFormat format = renderer->getPreferredCaptureFormat();
|
||||
|
||||
Image image(format, viewport[2], viewport[3]);
|
||||
if (!renderer->captureFrame(viewport[0], viewport[1],
|
||||
viewport[2], viewport[3],
|
||||
format, image.getPixels()))
|
||||
{
|
||||
fmt::print(cerr, _("Unable to capture a frame!\n"));
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
bool CelestiaCore::saveScreenShot(const fs::path& filename, ContentType type) const
|
||||
{
|
||||
if (type == Content_Unknown)
|
||||
type = DetermineFileType(filename);
|
||||
|
||||
// Get the dimensions of the current viewport
|
||||
array<int, 4> viewport;
|
||||
getRenderer()->getViewport(viewport);
|
||||
if (type != Content_JPEG && type != Content_PNG)
|
||||
{
|
||||
fmt::print(cerr, _("Unsupported image type: {}!\n"), filename.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
return CaptureBufferToFile(filename,
|
||||
viewport[0], viewport[1],
|
||||
viewport[2], viewport[3],
|
||||
getRenderer(),
|
||||
type);
|
||||
Image image = captureImage();
|
||||
if (!image.isValid())
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Content_JPEG:
|
||||
return SaveJPEGImage(filename, image);
|
||||
case Content_PNG:
|
||||
return SavePNGImage(filename, image);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CelestiaCore::setLogFile(fs::path &fn)
|
||||
|
|
|
@ -386,6 +386,7 @@ class CelestiaCore // : public Watchable<CelestiaCore>
|
|||
void setScriptHook(std::unique_ptr<celestia::scripts::IScriptHook> &&hook) { m_scriptHook = std::move(hook); }
|
||||
const std::shared_ptr<celestia::scripts::ScriptMaps>& scriptMaps() const { return m_scriptMaps; }
|
||||
|
||||
Image captureImage() const;
|
||||
bool saveScreenShot(const fs::path&, ContentType = Content_Unknown) const;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
// imagecapture.cpp
|
||||
//
|
||||
// Copyright (C) 2001-present, the Celestia Development Team
|
||||
// Original version by Chris Laurel <claurel@shatters.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <fmt/format.h>
|
||||
#include <celengine/render.h>
|
||||
#include <celimage/imageformats.h>
|
||||
#include <celutil/gettext.h>
|
||||
#include "imagecapture.h"
|
||||
|
||||
using fmt::print;
|
||||
using std::cerr;
|
||||
using std::unique_ptr;
|
||||
|
||||
bool CaptureBufferToFile(const fs::path& filename,
|
||||
int x, int y,
|
||||
int width, int height,
|
||||
const Renderer *renderer,
|
||||
ContentType type)
|
||||
{
|
||||
if (type != Content_JPEG && type != Content_PNG)
|
||||
{
|
||||
print(cerr, "Unsupported image type: {}!\n", filename.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef GL_ES
|
||||
int rowStride = width * 4;
|
||||
int imageSize = height * rowStride;
|
||||
Renderer::PixelFormat format = Renderer::PixelFormat::RGBA;
|
||||
#else
|
||||
int rowStride = (width * 3 + 3) & ~0x3;
|
||||
int imageSize = height * rowStride;
|
||||
Renderer::PixelFormat format = Renderer::PixelFormat::RGB;
|
||||
#endif
|
||||
auto pixels = unique_ptr<unsigned char[]>(new unsigned char[imageSize]);
|
||||
|
||||
if (!renderer->captureFrame(x, y, width, height,
|
||||
format, pixels.get(), true))
|
||||
{
|
||||
print(cerr, _("Unable to capture a frame!\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool removeAlpha = format == Renderer::PixelFormat::RGBA;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Content_JPEG:
|
||||
return SaveJPEGImage(filename, width, height, rowStride, pixels.get(), removeAlpha);
|
||||
case Content_PNG:
|
||||
return SavePNGImage(filename, width, height, rowStride, pixels.get(), removeAlpha);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
// imagecapture.h
|
||||
//
|
||||
// Copyright (C) 2001-present, the Celestia Development Team
|
||||
// Original version by Chris Laurel <claurel@shatters.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <celcompat/filesystem.h>
|
||||
#include <celutil/filetype.h>
|
||||
|
||||
class Renderer;
|
||||
|
||||
bool CaptureBufferToFile(const fs::path& filename,
|
||||
int x, int y,
|
||||
int width, int height,
|
||||
const Renderer *renderer,
|
||||
ContentType type);
|
|
@ -411,21 +411,8 @@ static void captureImage(AVFrame *pict, int width, int height, const Renderer *r
|
|||
x += (w - width) / 2;
|
||||
y += (h - height) / 2;
|
||||
r->captureFrame(x, y, width, height,
|
||||
Renderer::PixelFormat::RGB,
|
||||
r->getPreferredCaptureFormat(),
|
||||
pict->data[0]);
|
||||
|
||||
// Read image is vertically flipped
|
||||
// TODO: this should go to Renderer::captureFrame()
|
||||
int realWidth = width * 3; // 3 bytes per pixel
|
||||
uint8_t *tempLine = new uint8_t[realWidth];
|
||||
uint8_t *fb = pict->data[0];
|
||||
for (int i = 0, p = realWidth * (height - 1); i < p; i += realWidth, p -= realWidth)
|
||||
{
|
||||
memcpy(tempLine, &fb[i], realWidth);
|
||||
memcpy(&fb[i], &fb[p], realWidth);
|
||||
memcpy(&fb[p], tempLine, realWidth);
|
||||
}
|
||||
delete[] tempLine;
|
||||
}
|
||||
|
||||
// encode one video frame and send it to the muxer
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <celengine/glsupport.h>
|
||||
#include <celutil/gettext.h>
|
||||
#include <celutil/util.h>
|
||||
#include "qtappwin.h"
|
||||
|
@ -58,7 +59,6 @@
|
|||
#include "qteventfinder.h"
|
||||
#include "qtsettimedialog.h"
|
||||
#include "qtgotoobjectdialog.h"
|
||||
//#include "qtvideocapturedialog.h"
|
||||
#include <celestia/celestiastate.h>
|
||||
#include <celestia/scriptmenu.h>
|
||||
#include <celestia/url.h>
|
||||
|
@ -600,10 +600,7 @@ void CelestiaAppWindow::slotGrabImage()
|
|||
|
||||
if (!saveAsName.isEmpty())
|
||||
{
|
||||
//glWidget->repaint();
|
||||
QImage grabbedImage = glWidget->grabFrameBuffer();
|
||||
grabbedImage.save(saveAsName);
|
||||
|
||||
m_appCore->saveScreenShot(saveAsName.toStdString());
|
||||
settings.setValue("GrabImageDir", QFileInfo(saveAsName).absolutePath());
|
||||
}
|
||||
settings.endGroup();
|
||||
|
@ -695,13 +692,30 @@ void CelestiaAppWindow::slotCaptureVideo()
|
|||
#endif
|
||||
}
|
||||
|
||||
static QImage::Format toQFormat(PixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case PixelFormat::RGB:
|
||||
return QImage::Format_RGB888;
|
||||
case PixelFormat::RGBA:
|
||||
return QImage::Format_RGBA8888;
|
||||
default:
|
||||
return QImage::Format_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
void CelestiaAppWindow::slotCopyImage()
|
||||
{
|
||||
//glWidget->repaint();
|
||||
QImage grabbedImage = glWidget->grabFrameBuffer();
|
||||
Image image = m_appCore->captureImage();
|
||||
QImage grabbedImage = QImage(image.getPixels(),
|
||||
image.getWidth(),
|
||||
image.getHeight(),
|
||||
image.getPitch(),
|
||||
toQFormat(image.getFormat()));
|
||||
QApplication::clipboard()->setImage(grabbedImage);
|
||||
m_appCore->flash(QString(_("Captured screen shot to clipboard")).toStdString());
|
||||
m_appCore->flash(_("Captured screen shot to clipboard"));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1156,7 +1170,6 @@ void CelestiaAppWindow::createMenus()
|
|||
|
||||
QAction* captureVideoAction = new QAction(QIcon(":/icons/capture-video.png"),
|
||||
_("Capture &video"), this);
|
||||
// TODO: Add Mac support for video capture
|
||||
#ifndef USE_FFMPEG
|
||||
captureVideoAction->setEnabled(false);
|
||||
#endif
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include "celutil/filetype.h"
|
||||
#include "celutil/debug.h"
|
||||
#include "celutil/gettext.h"
|
||||
#include "celestia/imagecapture.h"
|
||||
#include "celestia/celestiacore.h"
|
||||
#include "celengine/simulation.h"
|
||||
#ifdef USE_GLCONTEXT
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
#include <cstdint>
|
||||
#include <fstream> // ifstream
|
||||
#include <iostream> // ios
|
||||
#include <celengine/glsupport.h>
|
||||
#include <celengine/image.h>
|
||||
#include <celutil/bytes.h>
|
||||
#include <celutil/debug.h>
|
||||
|
||||
using std::ifstream;
|
||||
using std::ios;
|
||||
using celestia::PixelFormat;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -126,7 +126,7 @@ Image* LoadBMPImage(ifstream& in)
|
|||
|
||||
// check for truncated file
|
||||
|
||||
auto* img = new Image(GL_RGB, imageHeader.width, imageHeader.height);
|
||||
auto* img = new Image(PixelFormat::RGB, imageHeader.width, imageHeader.height);
|
||||
|
||||
// Copy the image and perform any necessary conversions
|
||||
for (int32_t y = 0; y < imageHeader.height; y++)
|
||||
|
|
|
@ -280,7 +280,7 @@ Image* LoadDDSImage(const fs::path& filename)
|
|||
}
|
||||
}
|
||||
|
||||
Image *img = new Image(transparent0 ? GL_RGB : GL_RGBA, ddsd.width, ddsd.height);
|
||||
Image *img = new Image(transparent0 ? PixelFormat::RGB : PixelFormat::RGBA, ddsd.width, ddsd.height);
|
||||
memcpy(img->getPixels(), pixels, (transparent0 ? 3 : 4) * ddsd.width * ddsd.height);
|
||||
delete[] pixels;
|
||||
return img;
|
||||
|
@ -290,7 +290,7 @@ Image* LoadDDSImage(const fs::path& filename)
|
|||
// TODO: Verify that the reported texture size matches the amount of
|
||||
// data expected.
|
||||
|
||||
Image* img = new Image(format,
|
||||
Image* img = new Image(static_cast<PixelFormat>(format),
|
||||
(int) ddsd.width,
|
||||
(int) ddsd.height,
|
||||
max(ddsd.mipMapLevels, 1u));
|
||||
|
|
|
@ -18,6 +18,9 @@ Image* LoadBMPImage(const fs::path& filename);
|
|||
Image* LoadPNGImage(const fs::path& filename);
|
||||
Image* LoadDDSImage(const fs::path& filename);
|
||||
|
||||
bool SaveJPEGImage(const fs::path& filename, Image& image);
|
||||
bool SavePNGImage(const fs::path& filename, Image& image);
|
||||
|
||||
bool SaveJPEGImage(const fs::path& filename,
|
||||
int width, int height,
|
||||
int rowStride,
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
extern "C" {
|
||||
#include <jpeglib.h>
|
||||
}
|
||||
#include <celengine/glsupport.h>
|
||||
#include <celengine/image.h>
|
||||
#include <celutil/debug.h>
|
||||
|
||||
using celestia::PixelFormat;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct my_error_mgr
|
||||
|
@ -130,9 +131,9 @@ Image* LoadJPEGImage(const fs::path& filename, int /*unused*/)
|
|||
// Here we use the library's state variable cinfo.output_scanline as the
|
||||
// loop counter, so that we don't have to keep track ourselves.
|
||||
|
||||
int format = GL_RGB;
|
||||
PixelFormat format = PixelFormat::RGB;
|
||||
if (cinfo.output_components == 1)
|
||||
format = GL_LUMINANCE;
|
||||
format = PixelFormat::LUMINANCE;
|
||||
|
||||
img = new Image(format, cinfo.image_width, cinfo.image_height);
|
||||
|
||||
|
@ -216,7 +217,7 @@ bool SaveJPEGImage(const fs::path& filename,
|
|||
|
||||
while (cinfo.next_scanline < cinfo.image_height)
|
||||
{
|
||||
unsigned char *rowHead = &pixels[rowStride * (cinfo.image_height - cinfo.next_scanline - 1)];
|
||||
unsigned char *rowHead = &pixels[rowStride * cinfo.next_scanline];
|
||||
// Strip alpha values if we are in RGBA format
|
||||
if (removeAlpha)
|
||||
{
|
||||
|
@ -239,3 +240,13 @@ bool SaveJPEGImage(const fs::path& filename,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveJPEGImage(const fs::path& filename, Image& image)
|
||||
{
|
||||
return SaveJPEGImage(filename,
|
||||
image.getWidth(),
|
||||
image.getHeight(),
|
||||
image.getPitch(),
|
||||
image.getPixels(),
|
||||
image.hasAlpha());
|
||||
}
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
#include <png.h>
|
||||
#include <zlib.h>
|
||||
#include <fmt/printf.h>
|
||||
#include <celengine/glsupport.h>
|
||||
#include <celengine/image.h>
|
||||
#include <celutil/debug.h>
|
||||
#include <celutil/gettext.h>
|
||||
|
||||
using std::cerr;
|
||||
using std::clog;
|
||||
using celestia::PixelFormat;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -102,28 +102,28 @@ Image* LoadPNGImage(const fs::path& filename)
|
|||
&color_type, &interlace_type,
|
||||
nullptr, nullptr);
|
||||
|
||||
GLenum glformat = GL_RGB;
|
||||
PixelFormat format = PixelFormat::RGB;
|
||||
switch (color_type)
|
||||
{
|
||||
case PNG_COLOR_TYPE_GRAY:
|
||||
glformat = GL_LUMINANCE;
|
||||
format = PixelFormat::LUMINANCE;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
||||
glformat = GL_LUMINANCE_ALPHA;
|
||||
format = PixelFormat::LUM_ALPHA;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
glformat = GL_RGB;
|
||||
format = PixelFormat::RGB;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_PALETTE:
|
||||
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||
glformat = GL_RGBA;
|
||||
format = PixelFormat::RGBA;
|
||||
break;
|
||||
default:
|
||||
// badness
|
||||
break;
|
||||
}
|
||||
|
||||
img = new Image(glformat, width, height);
|
||||
img = new Image(format, width, height);
|
||||
|
||||
// TODO: consider using paletted textures if they're available
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
|
@ -184,7 +184,7 @@ bool SavePNGImage(const fs::path& filename,
|
|||
auto* row_pointers = new png_bytep[height];
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
unsigned char *rowHead = &pixels[rowStride * (height - i - 1)];
|
||||
unsigned char *rowHead = &pixels[rowStride * i];
|
||||
// Strip alpha values if we are in RGBA format
|
||||
if (removeAlpha)
|
||||
{
|
||||
|
@ -256,3 +256,14 @@ bool SavePNGImage(const fs::path& filename,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SavePNGImage(const fs::path& filename, Image& image)
|
||||
{
|
||||
return SavePNGImage(filename,
|
||||
image.getWidth(),
|
||||
image.getHeight(),
|
||||
image.getPitch(),
|
||||
image.getPixels(),
|
||||
image.hasAlpha());
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <celutil/debug.h>
|
||||
#include <celutil/gettext.h>
|
||||
#include <celestia/celestiacore.h>
|
||||
#include <celestia/imagecapture.h>
|
||||
#include <celestia/url.h>
|
||||
|
||||
#include "celx_internal.h"
|
||||
|
|
Loading…
Reference in New Issue