Files
2018-07-17 14:15:37 +02:00

272 lines
6.1 KiB
C++

#pragma once
#include "color.h"
#include "vec2.h"
#include "rect.h"
#include <string.h>
#include <cmath>
enum ColorFormat {
FMT_Grey,
FMT_Alpha,
FMT_RGB,
FMT_RGBA,
FMT_INVALID
};
enum ColorDepth {
DEPTH_Grey = sizeof(unsigned char),
DEPTH_Alpha = sizeof(unsigned char),
DEPTH_RGB = sizeof(ColorRGB),
DEPTH_RGBA = sizeof(Color)
};
//Color depths by format (e.g. ColorDepths[FMT_RGB] == DEPTH_RGB)
extern unsigned ColorDepths[4];
//Standard image in 32-bit RGBA format
struct Image {
union {
unsigned char* grey;
ColorRGB* rgb;
Color* rgba;
};
ColorFormat format;
unsigned int width, height;
unsigned char& get_alpha(unsigned int x, unsigned int y) {
return grey[x + (y * width)];
}
unsigned char get_alpha(unsigned int x, unsigned int y) const {
return grey[x + (y * width)];
}
unsigned char& get_grey(unsigned int x, unsigned int y) {
return grey[x + (y * width)];
}
unsigned char get_grey(unsigned int x, unsigned int y) const {
return grey[x + (y * width)];
}
ColorRGB& get_rgb(unsigned int x, unsigned int y) {
return rgb[x + (y * width)];
}
ColorRGB get_rgb(unsigned int x, unsigned int y) const {
return rgb[x + (y * width)];
}
Color& get_rgba(unsigned int x, unsigned int y) {
return rgba[x + (y * width)];
}
Color get_rgba(unsigned int x, unsigned int y) const {
return rgba[x + (y * width)];
}
Color get(unsigned int x, unsigned int y) const {
switch(format) {
case FMT_Grey:
case FMT_Alpha:
return grey[x + (y * width)];
case FMT_RGB:
return rgb[x + (y * width)];
case FMT_RGBA:
return rgba[x + (y * width)];
}
return Color();
}
Color getTexel(float x, float y) {
float ipart;
float u = std::modf(x,&ipart), v = std::modf(y,&ipart);
if(u < 0)
u = 1.f + u;
u *= (float)width;
if(v < 0)
v = 1.f + v;
v *= (float)height;
int x1 = (int)u;
int x2 = (x1 + 1) % (int)width;
int y1 = (int)v;
int y2 = (y1 + 1) % (int)height;
Color upper = get(x1,y1).getInterpolated(get(x2,y1), std::modf(u,&ipart));
Color lower = get(x1,y2).getInterpolated(get(x2,y2), std::modf(u,&ipart));
return upper.getInterpolated(lower, std::modf(v,&ipart));
}
Color getTexel(const vec2f& pos) {
return getTexel(pos.x, pos.y);
}
static Image* random(const vec2u& resolution, int(int,int));
//Returns an image that is a sphere-distortion-corrected version of this one
Image* sphereDistort() const;
//Returns a half-resolution version of this image
//If the image is too small, returns 0
Image* makeMipmap() const;
Image* crop(const rect<unsigned>& bound) const;
Image() : width(0), height(0), rgba(0) {}
Image(const Image& other) : width(0), height(0), rgba(0) {
*this = other;
}
void operator=(const Image& other) {
if(other.width != width || other.height != height || other.format != format) {
width = other.width;
height = other.height;
format = other.format;
switch(format) {
case FMT_Grey:
case FMT_Alpha:
delete[] grey;
grey = new unsigned char[width*height];
break;
case FMT_RGB:
delete[] rgb;
rgb = new ColorRGB[width*height];
break;
case FMT_RGBA:
delete[] rgba;
rgba = new Color[width*height];
break;
}
}
switch(format) {
case FMT_Grey:
case FMT_Alpha:
memcpy(grey, other.grey, width*height*sizeof(unsigned char));
break;
case FMT_RGB:
memcpy(rgb, other.rgb, width*height*sizeof(ColorRGB));
break;
case FMT_RGBA:
memcpy(rgba, other.rgba, width*height*sizeof(Color));
break;
}
}
Image(unsigned int Width, unsigned int Height, ColorFormat Format)
: width(Width), height(Height), format(Format), rgba(0)
{
if(width > 0 && height > 0) {
switch(Format) {
case FMT_Grey:
case FMT_Alpha:
grey = new unsigned char[Width*Height]; break;
case FMT_RGB:
rgb = new ColorRGB[Width*Height]; break;
case FMT_RGBA:
rgba = new Color[Width*Height]; break;
}
}
}
Image(unsigned int Width, unsigned int Height, ColorFormat Format, unsigned char defaultLevel)
: width(Width), height(Height), format(Format), rgba(0)
{
if(width > 0 && height > 0) {
switch(Format) {
case FMT_Grey:
case FMT_Alpha:
grey = new unsigned char[Width*Height]; break;
case FMT_RGB:
rgb = new ColorRGB[Width*Height]; break;
case FMT_RGBA:
rgba = new Color[Width*Height]; break;
}
if(grey)
memset(grey, defaultLevel, Width * Height * ColorDepths[format]);
}
}
void makeRGBA(const Image& other) {
resize(other.width, other.height, FMT_RGBA);
switch(other.format) {
case FMT_Grey:
case FMT_Alpha:
for(size_t i = 0, sz = other.width * other.height; i < sz; ++i) {
Color& col = rgba[i];
col.r = other.grey[i];
col.g = col.r;
col.b = col.r;
col.a = 0xff;
}
break;
case FMT_RGB:
for(size_t i = 0, sz = other.width * other.height; i < sz; ++i) {
Color& col = rgba[i];
ColorRGB& ocol = other.rgb[i];
col.r = ocol.r;
col.g = ocol.g;
col.b = ocol.b;
col.a = 0xff;
}
break;
case FMT_RGBA:
for(size_t i = 0, sz = other.width * other.height; i < sz; ++i)
rgba[i] = other.rgba[i];
break;
}
}
void resize(unsigned Width, unsigned Height, ColorFormat newFormat = FMT_INVALID) {
if(newFormat == FMT_INVALID)
newFormat = format;
width = Width;
height = Height;
switch(format) {
case FMT_Grey:
case FMT_Alpha:
delete[] grey; break;
case FMT_RGB:
delete[] rgb; break;
case FMT_RGBA:
delete[] rgba; break;
}
if(width > 0 && height > 0) {
format = newFormat;
switch(format) {
case FMT_Grey:
case FMT_Alpha:
grey = new unsigned char[Width*Height]; break;
case FMT_RGB:
rgb = new ColorRGB[Width*Height]; break;
case FMT_RGBA:
rgba = new Color[Width*Height]; break;
}
}
}
~Image() {
switch(format) {
case FMT_Grey:
case FMT_Alpha:
delete[] grey; break;
case FMT_RGB:
delete[] rgb; break;
case FMT_RGBA:
delete[] rgba; break;
}
}
};
//Attempts to load the file according to the extension. If the file cannot be loaded, 0 is returned.
Image* loadImage(const char* filename);
bool saveImage(const Image* img, const char* filename, bool flip = false);