3e12aff909
DXT Testing:
The modified algorithm has been tested on the Kodak test set using 64-bit build with default settings (running on Windows 10, i7-4790, 3.6GHz). All the decompressed test images are identical to the images being compressed and decompressed using original version of Crunch (revision ea9b8d8).
[Compressing Kodak set without mipmaps using DXT1 encoding]
Original: 1582222 bytes / 28.866 sec
Modified: 1468204 bytes / 11.858 sec
Improvement: 7.21% (compression ratio) / 58.92% (compression time)
[Compressing Kodak set with mipmaps using DXT1 encoding]
Original: 2065243 bytes / 36.878 sec
Modified: 1914805 bytes / 15.625 sec
Improvement: 7.28% (compression ratio) / 57.63% (compression time)
ETC Testing:
The modified algorithm has been tested on the Kodak test set using 64-bit build with default settings (running on Windows 10, i7-4790, 3.6GHz). The ETC1 quantization parameters have been selected in such a way, so that ETC1 compression gives approximately the same average Luma PSNR as the corresponding DXT1 compression (which is equal to 34.044 dB for the Kodak test set compressed without mipmaps using DXT1 encoding and default quality settings).
[Compressing Kodak set without mipmaps using ETC1 encoding]
Total size: 1607858 bytes
Total time: 17.181 sec
Average bitrate: 1.363 bpp
Average Luma PSNR: 34.050 dB
636 lines
19 KiB
C++
636 lines
19 KiB
C++
// File: crn_image.h
|
|
// See Copyright Notice and license at the end of inc/crnlib.h
|
|
#pragma once
|
|
#include "crn_color.h"
|
|
#include "crn_vec.h"
|
|
#include "crn_pixel_format.h"
|
|
#include "crn_rect.h"
|
|
|
|
namespace crnlib {
|
|
template <typename color_type>
|
|
class image {
|
|
public:
|
|
typedef color_type color_t;
|
|
|
|
typedef crnlib::vector<color_type> pixel_buf_t;
|
|
|
|
image()
|
|
: m_width(0),
|
|
m_height(0),
|
|
m_pitch(0),
|
|
m_total(0),
|
|
m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
|
|
m_pPixels(NULL) {
|
|
}
|
|
|
|
// pitch is in PIXELS, not bytes.
|
|
image(uint width, uint height, uint pitch = UINT_MAX, const color_type& background = color_type::make_black(), uint flags = pixel_format_helpers::cDefaultCompFlags)
|
|
: m_comp_flags(flags) {
|
|
CRNLIB_ASSERT((width > 0) && (height > 0));
|
|
if (pitch == UINT_MAX)
|
|
pitch = width;
|
|
|
|
m_pixel_buf.resize(pitch * height);
|
|
|
|
m_width = width;
|
|
m_height = height;
|
|
m_pitch = pitch;
|
|
m_total = m_pitch * m_height;
|
|
|
|
m_pPixels = &m_pixel_buf.front();
|
|
|
|
set_all(background);
|
|
}
|
|
|
|
// pitch is in PIXELS, not bytes.
|
|
image(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags) {
|
|
alias(pPixels, width, height, pitch, flags);
|
|
}
|
|
|
|
image& operator=(const image& other) {
|
|
if (this == &other)
|
|
return *this;
|
|
|
|
if (other.m_pixel_buf.empty()) {
|
|
// This doesn't look very safe - let's make a new instance.
|
|
//m_pixel_buf.clear();
|
|
//m_pPixels = other.m_pPixels;
|
|
|
|
const uint total_pixels = other.m_pitch * other.m_height;
|
|
if ((total_pixels) && (other.m_pPixels)) {
|
|
m_pixel_buf.resize(total_pixels);
|
|
m_pixel_buf.insert(0, other.m_pPixels, m_pixel_buf.size());
|
|
m_pPixels = &m_pixel_buf.front();
|
|
} else {
|
|
m_pixel_buf.clear();
|
|
m_pPixels = NULL;
|
|
}
|
|
} else {
|
|
m_pixel_buf = other.m_pixel_buf;
|
|
m_pPixels = &m_pixel_buf.front();
|
|
}
|
|
|
|
m_width = other.m_width;
|
|
m_height = other.m_height;
|
|
m_pitch = other.m_pitch;
|
|
m_total = other.m_total;
|
|
m_comp_flags = other.m_comp_flags;
|
|
|
|
return *this;
|
|
}
|
|
|
|
image(const image& other)
|
|
: m_width(0), m_height(0), m_pitch(0), m_total(0), m_comp_flags(pixel_format_helpers::cDefaultCompFlags), m_pPixels(NULL) {
|
|
*this = other;
|
|
}
|
|
|
|
// pitch is in PIXELS, not bytes.
|
|
void alias(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags) {
|
|
m_pixel_buf.clear();
|
|
|
|
m_pPixels = pPixels;
|
|
|
|
m_width = width;
|
|
m_height = height;
|
|
m_pitch = (pitch == UINT_MAX) ? width : pitch;
|
|
m_total = m_pitch * m_height;
|
|
m_comp_flags = flags;
|
|
}
|
|
|
|
// pitch is in PIXELS, not bytes.
|
|
bool grant_ownership(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags) {
|
|
if (pitch == UINT_MAX)
|
|
pitch = width;
|
|
|
|
if ((!pPixels) || (!width) || (!height) || (pitch < width)) {
|
|
CRNLIB_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
if (pPixels == get_ptr()) {
|
|
CRNLIB_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
clear();
|
|
|
|
if (!m_pixel_buf.grant_ownership(pPixels, height * pitch, height * pitch))
|
|
return false;
|
|
|
|
m_pPixels = pPixels;
|
|
|
|
m_width = width;
|
|
m_height = height;
|
|
m_pitch = pitch;
|
|
m_total = pitch * height;
|
|
m_comp_flags = flags;
|
|
|
|
return true;
|
|
}
|
|
|
|
void clear() {
|
|
m_pPixels = NULL;
|
|
m_pixel_buf.clear();
|
|
m_width = 0;
|
|
m_height = 0;
|
|
m_pitch = 0;
|
|
m_total = 0;
|
|
m_comp_flags = pixel_format_helpers::cDefaultCompFlags;
|
|
}
|
|
|
|
inline bool is_valid() const { return m_total > 0; }
|
|
|
|
inline pixel_format_helpers::component_flags get_comp_flags() const { return static_cast<pixel_format_helpers::component_flags>(m_comp_flags); }
|
|
inline void set_comp_flags(pixel_format_helpers::component_flags new_flags) { m_comp_flags = new_flags; }
|
|
inline void reset_comp_flags() { m_comp_flags = pixel_format_helpers::cDefaultCompFlags; }
|
|
|
|
inline bool is_component_valid(uint index) const {
|
|
CRNLIB_ASSERT(index < 4U);
|
|
return utils::is_flag_set(m_comp_flags, index);
|
|
}
|
|
inline void set_component_valid(uint index, bool state) {
|
|
CRNLIB_ASSERT(index < 4U);
|
|
utils::set_flag(m_comp_flags, index, state);
|
|
}
|
|
|
|
inline bool has_rgb() const { return is_component_valid(0) || is_component_valid(1) || is_component_valid(2); }
|
|
inline bool has_alpha() const { return is_component_valid(3); }
|
|
|
|
inline bool is_grayscale() const { return utils::is_bit_set(m_comp_flags, pixel_format_helpers::cCompFlagGrayscale); }
|
|
inline void set_grayscale(bool state) { utils::set_bit(m_comp_flags, pixel_format_helpers::cCompFlagGrayscale, state); }
|
|
|
|
void set_all(const color_type& c) {
|
|
for (uint i = 0; i < m_total; i++)
|
|
m_pPixels[i] = c;
|
|
}
|
|
|
|
void flip_x() {
|
|
const uint half_width = m_width / 2;
|
|
for (uint y = 0; y < m_height; y++) {
|
|
for (uint x = 0; x < half_width; x++) {
|
|
color_type c((*this)(x, y));
|
|
(*this)(x, y) = (*this)(m_width - 1 - x, y);
|
|
(*this)(m_width - 1 - x, y) = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
void flip_y() {
|
|
const uint half_height = m_height / 2;
|
|
for (uint y = 0; y < half_height; y++) {
|
|
for (uint x = 0; x < m_width; x++) {
|
|
color_type c((*this)(x, y));
|
|
(*this)(x, y) = (*this)(x, m_height - 1 - y);
|
|
(*this)(x, m_height - 1 - y) = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
void convert_to_grayscale() {
|
|
for (uint y = 0; y < m_height; y++)
|
|
for (uint x = 0; x < m_width; x++) {
|
|
color_type c((*this)(x, y));
|
|
typename color_type::component_t l = static_cast<typename color_type::component_t>(c.get_luma());
|
|
c.r = l;
|
|
c.g = l;
|
|
c.b = l;
|
|
(*this)(x, y) = c;
|
|
}
|
|
|
|
set_grayscale(true);
|
|
}
|
|
|
|
void swizzle(uint r, uint g, uint b, uint a) {
|
|
for (uint y = 0; y < m_height; y++)
|
|
for (uint x = 0; x < m_width; x++) {
|
|
const color_type& c = (*this)(x, y);
|
|
|
|
(*this)(x, y) = color_type(c[r], c[g], c[b], c[a]);
|
|
}
|
|
}
|
|
|
|
void set_alpha_to_luma() {
|
|
for (uint y = 0; y < m_height; y++)
|
|
for (uint x = 0; x < m_width; x++) {
|
|
color_type c((*this)(x, y));
|
|
typename color_type::component_t l = static_cast<typename color_type::component_t>(c.get_luma());
|
|
c.a = l;
|
|
(*this)(x, y) = c;
|
|
}
|
|
|
|
set_component_valid(3, true);
|
|
}
|
|
|
|
bool extract_block(color_type* pDst, uint x, uint y, uint w, uint h, bool flip_xy = false) const {
|
|
if ((x >= m_width) || (y >= m_height)) {
|
|
CRNLIB_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
if (flip_xy) {
|
|
for (uint y_ofs = 0; y_ofs < h; y_ofs++)
|
|
for (uint x_ofs = 0; x_ofs < w; x_ofs++)
|
|
pDst[x_ofs * h + y_ofs] = get_clamped(x_ofs + x, y_ofs + y); // 5/4/12 - this was incorrectly x_ofs * 4
|
|
} else if (((x + w) > m_width) || ((y + h) > m_height)) {
|
|
for (uint y_ofs = 0; y_ofs < h; y_ofs++)
|
|
for (uint x_ofs = 0; x_ofs < w; x_ofs++)
|
|
*pDst++ = get_clamped(x_ofs + x, y_ofs + y);
|
|
} else {
|
|
const color_type* pSrc = get_scanline(y) + x;
|
|
|
|
for (uint i = h; i; i--) {
|
|
memcpy(pDst, pSrc, w * sizeof(color_type));
|
|
pDst += w;
|
|
|
|
pSrc += m_pitch;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// No clipping!
|
|
void unclipped_fill_box(uint x, uint y, uint w, uint h, const color_type& c) {
|
|
if (((x + w) > m_width) || ((y + h) > m_height)) {
|
|
CRNLIB_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
color_type* p = get_scanline(y) + x;
|
|
|
|
for (uint i = h; i; i--) {
|
|
color_type* q = p;
|
|
for (uint j = w; j; j--)
|
|
*q++ = c;
|
|
p += m_pitch;
|
|
}
|
|
}
|
|
|
|
void draw_rect(int x, int y, uint width, uint height, const color_type& c) {
|
|
draw_line(x, y, x + width - 1, y, c);
|
|
draw_line(x, y, x, y + height - 1, c);
|
|
draw_line(x + width - 1, y, x + width - 1, y + height - 1, c);
|
|
draw_line(x, y + height - 1, x + width - 1, y + height - 1, c);
|
|
}
|
|
|
|
// No clipping!
|
|
bool unclipped_blit(uint src_x, uint src_y, uint src_w, uint src_h, uint dst_x, uint dst_y, const image& src) {
|
|
if ((!is_valid()) || (!src.is_valid())) {
|
|
CRNLIB_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
if (((src_x + src_w) > src.get_width()) || ((src_y + src_h) > src.get_height())) {
|
|
CRNLIB_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
if (((dst_x + src_w) > get_width()) || ((dst_y + src_h) > get_height())) {
|
|
CRNLIB_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
const color_type* pS = &src(src_x, src_y);
|
|
color_type* pD = &(*this)(dst_x, dst_y);
|
|
|
|
const uint bytes_to_copy = src_w * sizeof(color_type);
|
|
for (uint i = src_h; i; i--) {
|
|
memcpy(pD, pS, bytes_to_copy);
|
|
|
|
pS += src.get_pitch();
|
|
pD += get_pitch();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// With clipping.
|
|
bool blit(int dst_x, int dst_y, const image& src) {
|
|
if ((!is_valid()) || (!src.is_valid())) {
|
|
CRNLIB_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
int src_x = 0;
|
|
int src_y = 0;
|
|
|
|
if (dst_x < 0) {
|
|
src_x = -dst_x;
|
|
if (src_x >= static_cast<int>(src.get_width()))
|
|
return false;
|
|
dst_x = 0;
|
|
}
|
|
|
|
if (dst_y < 0) {
|
|
src_y = -dst_y;
|
|
if (src_y >= static_cast<int>(src.get_height()))
|
|
return false;
|
|
dst_y = 0;
|
|
}
|
|
|
|
if ((dst_x >= (int)m_width) || (dst_y >= (int)m_height))
|
|
return false;
|
|
|
|
uint width = math::minimum(m_width - dst_x, src.get_width() - src_x);
|
|
uint height = math::minimum(m_height - dst_y, src.get_height() - src_y);
|
|
|
|
bool success = unclipped_blit(src_x, src_y, width, height, dst_x, dst_y, src);
|
|
(void)success;
|
|
CRNLIB_ASSERT(success);
|
|
|
|
return true;
|
|
}
|
|
|
|
// With clipping.
|
|
bool blit(int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, const image& src) {
|
|
if ((!is_valid()) || (!src.is_valid())) {
|
|
CRNLIB_ASSERT(0);
|
|
return false;
|
|
}
|
|
|
|
rect src_rect(src_x, src_y, src_x + src_w, src_y + src_h);
|
|
if (!src_rect.intersect(src.get_bounds()))
|
|
return false;
|
|
|
|
rect dst_rect(dst_x, dst_y, dst_x + src_rect.get_width(), dst_y + src_rect.get_height());
|
|
if (!dst_rect.intersect(get_bounds()))
|
|
return false;
|
|
|
|
bool success = unclipped_blit(
|
|
src_rect.get_left(), src_rect.get_top(),
|
|
math::minimum(src_rect.get_width(), dst_rect.get_width()), math::minimum(src_rect.get_height(), dst_rect.get_height()),
|
|
dst_rect.get_left(), dst_rect.get_top(), src);
|
|
(void)success;
|
|
CRNLIB_ASSERT(success);
|
|
|
|
return true;
|
|
}
|
|
|
|
// In-place resize of image dimensions (cropping).
|
|
bool resize(uint new_width, uint new_height, uint new_pitch = UINT_MAX, const color_type background = color_type::make_black()) {
|
|
if (new_pitch == UINT_MAX)
|
|
new_pitch = new_width;
|
|
|
|
if ((new_width == m_width) && (new_height == m_height) && (new_pitch == m_pitch))
|
|
return true;
|
|
|
|
if ((!new_width) || (!new_height) || (!new_pitch)) {
|
|
clear();
|
|
return false;
|
|
}
|
|
|
|
pixel_buf_t existing_pixels;
|
|
existing_pixels.swap(m_pixel_buf);
|
|
|
|
if (!m_pixel_buf.try_resize(new_height * new_pitch)) {
|
|
clear();
|
|
return false;
|
|
}
|
|
|
|
for (uint y = 0; y < new_height; y++) {
|
|
for (uint x = 0; x < new_width; x++) {
|
|
if ((x < m_width) && (y < m_height))
|
|
m_pixel_buf[x + y * new_pitch] = existing_pixels[x + y * m_pitch];
|
|
else
|
|
m_pixel_buf[x + y * new_pitch] = background;
|
|
}
|
|
}
|
|
|
|
m_width = new_width;
|
|
m_height = new_height;
|
|
m_pitch = new_pitch;
|
|
m_total = new_pitch * new_height;
|
|
m_pPixels = &m_pixel_buf.front();
|
|
|
|
return true;
|
|
}
|
|
|
|
inline uint get_width() const { return m_width; }
|
|
inline uint get_height() const { return m_height; }
|
|
inline uint get_total_pixels() const { return m_width * m_height; }
|
|
|
|
inline rect get_bounds() const { return rect(0, 0, m_width, m_height); }
|
|
|
|
inline uint get_pitch() const { return m_pitch; }
|
|
inline uint get_pitch_in_bytes() const { return m_pitch * sizeof(color_type); }
|
|
|
|
// Returns pitch * height, NOT width * height!
|
|
inline uint get_total() const { return m_total; }
|
|
|
|
inline uint get_block_width(uint block_size) const { return (m_width + block_size - 1) / block_size; }
|
|
inline uint get_block_height(uint block_size) const { return (m_height + block_size - 1) / block_size; }
|
|
inline uint get_total_blocks(uint block_size) const { return get_block_width(block_size) * get_block_height(block_size); }
|
|
|
|
inline uint get_size_in_bytes() const { return sizeof(color_type) * m_total; }
|
|
|
|
inline const color_type* get_pixels() const { return m_pPixels; }
|
|
inline color_type* get_pixels() { return m_pPixels; }
|
|
|
|
inline const color_type& operator()(uint x, uint y) const {
|
|
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
|
return m_pPixels[x + y * m_pitch];
|
|
}
|
|
|
|
inline color_type& operator()(uint x, uint y) {
|
|
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
|
return m_pPixels[x + y * m_pitch];
|
|
}
|
|
|
|
inline const color_type& get_unclamped(uint x, uint y) const {
|
|
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
|
return m_pPixels[x + y * m_pitch];
|
|
}
|
|
|
|
inline const color_type& get_clamped(int x, int y) const {
|
|
x = math::clamp<int>(x, 0, m_width - 1);
|
|
y = math::clamp<int>(y, 0, m_height - 1);
|
|
return m_pPixels[x + y * m_pitch];
|
|
}
|
|
|
|
// Sample image with bilinear filtering.
|
|
// (x,y) - Continuous coordinates, where pixel centers are at (.5,.5), valid image coords are [0,width] and [0,height].
|
|
void get_filtered(float x, float y, color_type& result) const {
|
|
x -= .5f;
|
|
y -= .5f;
|
|
|
|
int ix = (int)floor(x);
|
|
int iy = (int)floor(y);
|
|
float wx = x - ix;
|
|
float wy = y - iy;
|
|
|
|
color_type a(get_clamped(ix, iy));
|
|
color_type b(get_clamped(ix + 1, iy));
|
|
color_type c(get_clamped(ix, iy + 1));
|
|
color_type d(get_clamped(ix + 1, iy + 1));
|
|
|
|
for (uint i = 0; i < 4; i++) {
|
|
double top = math::lerp<double>(a[i], b[i], wx);
|
|
double bot = math::lerp<double>(c[i], d[i], wx);
|
|
double m = math::lerp<double>(top, bot, wy);
|
|
|
|
if (!color_type::component_traits::cFloat)
|
|
m += .5f;
|
|
|
|
result.set_component(i, static_cast<typename color_type::parameter_t>(m));
|
|
}
|
|
}
|
|
|
|
void get_filtered(float x, float y, vec4F& result) const {
|
|
x -= .5f;
|
|
y -= .5f;
|
|
|
|
int ix = (int)floor(x);
|
|
int iy = (int)floor(y);
|
|
float wx = x - ix;
|
|
float wy = y - iy;
|
|
|
|
color_type a(get_clamped(ix, iy));
|
|
color_type b(get_clamped(ix + 1, iy));
|
|
color_type c(get_clamped(ix, iy + 1));
|
|
color_type d(get_clamped(ix + 1, iy + 1));
|
|
|
|
for (uint i = 0; i < 4; i++) {
|
|
float top = math::lerp<float>(a[i], b[i], wx);
|
|
float bot = math::lerp<float>(c[i], d[i], wx);
|
|
float m = math::lerp<float>(top, bot, wy);
|
|
|
|
result[i] = m;
|
|
}
|
|
}
|
|
|
|
inline void set_pixel_unclipped(uint x, uint y, const color_type& c) {
|
|
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
|
m_pPixels[x + y * m_pitch] = c;
|
|
}
|
|
|
|
inline void set_pixel_clipped(int x, int y, const color_type& c) {
|
|
if ((static_cast<uint>(x) >= m_width) || (static_cast<uint>(y) >= m_height))
|
|
return;
|
|
|
|
m_pPixels[x + y * m_pitch] = c;
|
|
}
|
|
|
|
inline const color_type* get_scanline(uint y) const {
|
|
CRNLIB_ASSERT(y < m_height);
|
|
return &m_pPixels[y * m_pitch];
|
|
}
|
|
|
|
inline color_type* get_scanline(uint y) {
|
|
CRNLIB_ASSERT(y < m_height);
|
|
return &m_pPixels[y * m_pitch];
|
|
}
|
|
|
|
inline const color_type* get_ptr() const {
|
|
return m_pPixels;
|
|
}
|
|
|
|
inline color_type* get_ptr() {
|
|
return m_pPixels;
|
|
}
|
|
|
|
inline void swap(image& other) {
|
|
utils::swap(m_width, other.m_width);
|
|
utils::swap(m_height, other.m_height);
|
|
utils::swap(m_pitch, other.m_pitch);
|
|
utils::swap(m_total, other.m_total);
|
|
utils::swap(m_comp_flags, other.m_comp_flags);
|
|
utils::swap(m_pPixels, other.m_pPixels);
|
|
m_pixel_buf.swap(other.m_pixel_buf);
|
|
}
|
|
|
|
void draw_line(int xs, int ys, int xe, int ye, const color_type& color) {
|
|
if (xs > xe) {
|
|
utils::swap(xs, xe);
|
|
utils::swap(ys, ye);
|
|
}
|
|
|
|
int dx = xe - xs, dy = ye - ys;
|
|
if (!dx) {
|
|
if (ys > ye)
|
|
utils::swap(ys, ye);
|
|
for (int i = ys; i <= ye; i++)
|
|
set_pixel_clipped(xs, i, color);
|
|
} else if (!dy) {
|
|
for (int i = xs; i < xe; i++)
|
|
set_pixel_clipped(i, ys, color);
|
|
} else if (dy > 0) {
|
|
if (dy <= dx) {
|
|
int e = 2 * dy - dx, e_no_inc = 2 * dy, e_inc = 2 * (dy - dx);
|
|
rasterize_line(xs, ys, xe, ye, 0, 1, e, e_inc, e_no_inc, color);
|
|
} else {
|
|
int e = 2 * dx - dy, e_no_inc = 2 * dx, e_inc = 2 * (dx - dy);
|
|
rasterize_line(xs, ys, xe, ye, 1, 1, e, e_inc, e_no_inc, color);
|
|
}
|
|
} else {
|
|
dy = -dy;
|
|
if (dy <= dx) {
|
|
int e = 2 * dy - dx, e_no_inc = 2 * dy, e_inc = 2 * (dy - dx);
|
|
rasterize_line(xs, ys, xe, ye, 0, -1, e, e_inc, e_no_inc, color);
|
|
} else {
|
|
int e = 2 * dx - dy, e_no_inc = (2 * dx), e_inc = 2 * (dx - dy);
|
|
rasterize_line(xe, ye, xs, ys, 1, -1, e, e_inc, e_no_inc, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
const pixel_buf_t& get_pixel_buf() const { return m_pixel_buf; }
|
|
pixel_buf_t& get_pixel_buf() { return m_pixel_buf; }
|
|
|
|
private:
|
|
uint m_width;
|
|
uint m_height;
|
|
uint m_pitch;
|
|
uint m_total;
|
|
uint m_comp_flags;
|
|
|
|
color_type* m_pPixels;
|
|
|
|
pixel_buf_t m_pixel_buf;
|
|
|
|
void rasterize_line(int xs, int ys, int xe, int ye, int pred, int inc_dec, int e, int e_inc, int e_no_inc, const color_type& color) {
|
|
int start, end, var;
|
|
|
|
if (pred) {
|
|
start = ys;
|
|
end = ye;
|
|
var = xs;
|
|
for (int i = start; i <= end; i++) {
|
|
set_pixel_clipped(var, i, color);
|
|
if (e < 0)
|
|
e += e_no_inc;
|
|
else {
|
|
var += inc_dec;
|
|
e += e_inc;
|
|
}
|
|
}
|
|
} else {
|
|
start = xs;
|
|
end = xe;
|
|
var = ys;
|
|
for (int i = start; i <= end; i++) {
|
|
set_pixel_clipped(i, var, color);
|
|
if (e < 0)
|
|
e += e_no_inc;
|
|
else {
|
|
var += inc_dec;
|
|
e += e_inc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
typedef image<color_quad_u8> image_u8;
|
|
typedef image<color_quad_i16> image_i16;
|
|
typedef image<color_quad_u16> image_u16;
|
|
typedef image<color_quad_i32> image_i32;
|
|
typedef image<color_quad_u32> image_u32;
|
|
typedef image<color_quad_f> image_f;
|
|
|
|
template <typename color_type>
|
|
inline void swap(image<color_type>& a, image<color_type>& b) {
|
|
a.swap(b);
|
|
}
|
|
|
|
} // namespace crnlib
|