Implemented Floyd Steinberg dithering and made it default.
Renamed many constants and fields according to Open BeOS coding style guide. Removed unused code that handles color spaces other than B_RGB32. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6432 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
d7f45f5855
commit
7ea239b20d
@ -8,21 +8,38 @@
|
||||
|
||||
#include <GraphicsDefs.h>
|
||||
|
||||
struct CACHE_FOR_CMAP8 {
|
||||
uint density;
|
||||
bool hit;
|
||||
};
|
||||
// definition for B_RGB32 (=B_RGB32_LITTLE) and B_RGBA32
|
||||
typedef struct {
|
||||
uchar blue;
|
||||
uchar green;
|
||||
uchar red;
|
||||
uchar alpha; // unused in B_RGB32
|
||||
} ColorRGB32Little;
|
||||
|
||||
// definition for B_RGB32_BIG and B_RGBA32_BIG
|
||||
typedef struct {
|
||||
uchar alpha; // unused in B_RGB32_BIG
|
||||
uchar red;
|
||||
uchar green;
|
||||
uchar blue;
|
||||
} ColorRGB32Big;
|
||||
|
||||
typedef union {
|
||||
ColorRGB32Little little;
|
||||
ColorRGB32Big big;
|
||||
} ColorRGB32;
|
||||
|
||||
class Halftone;
|
||||
typedef int (Halftone::*PFN_dither)(uchar *dst, const uchar *src, int x, int y, int width);
|
||||
typedef uint (*PFN_gray)(rgb_color c);
|
||||
typedef uint (*PFN_gray)(ColorRGB32 c);
|
||||
|
||||
class Halftone {
|
||||
public:
|
||||
enum DITHERTYPE {
|
||||
TYPE1,
|
||||
TYPE2,
|
||||
TYPE3
|
||||
enum DitherType {
|
||||
kType1,
|
||||
kType2,
|
||||
kType3,
|
||||
kTypeFloydSteinberg,
|
||||
};
|
||||
enum GrayFunction {
|
||||
kMixToGray,
|
||||
@ -30,65 +47,85 @@ public:
|
||||
kGreenChannel,
|
||||
kBlueChannel
|
||||
};
|
||||
Halftone(color_space cs, double gamma = 1.4, DITHERTYPE dither_type = TYPE3);
|
||||
enum Planes {
|
||||
kPlaneMonochrome1, // 1 bit depth (0 white, 1 black)
|
||||
kPlaneRGB1, // 3 planes, 1 bit depth (0 black, 7 white)
|
||||
};
|
||||
Halftone(color_space cs, double gamma = 1.4, DitherType dither_type = kTypeFloydSteinberg);
|
||||
~Halftone();
|
||||
void setPlanes(Planes planes);
|
||||
int dither(uchar *dst, const uchar *src, int x, int y, int width);
|
||||
int getPixelDepth() const;
|
||||
const rgb_color *getPalette() const;
|
||||
const uchar *getPattern() const;
|
||||
void setPattern(const uchar *pattern);
|
||||
|
||||
protected:
|
||||
PFN_gray getGrayFunction() const;
|
||||
void setGrayFunction(PFN_gray gray);
|
||||
void setGrayFunction(GrayFunction grayFunction);
|
||||
|
||||
protected:
|
||||
void createGammaTable(double gamma);
|
||||
void initElements(int x, int y, uchar *elements);
|
||||
int ditherGRAY1(uchar *dst, const uchar *src, int x, int y, int width);
|
||||
int ditherGRAY8(uchar *dst, const uchar *src, int x, int y, int width);
|
||||
int ditherCMAP8(uchar *dst, const uchar *src, int x, int y, int width);
|
||||
uint getDensity(ColorRGB32 c) const;
|
||||
int ditherRGB32(uchar *dst, const uchar *src, int x, int y, int width);
|
||||
|
||||
void initFloydSteinberg();
|
||||
void deleteErrorTables();
|
||||
void uninitFloydSteinberg();
|
||||
void setupErrorBuffer(int x, int y, int width);
|
||||
int ditherFloydSteinberg(uchar *dst, const uchar* src, int x, int y, int width);
|
||||
|
||||
Halftone(const Halftone &);
|
||||
Halftone &operator = (const Halftone &);
|
||||
|
||||
private:
|
||||
PFN_dither __dither;
|
||||
PFN_gray __gray;
|
||||
int __pixel_depth;
|
||||
const rgb_color *__palette;
|
||||
const uchar *__pattern;
|
||||
uint __gamma_table[256];
|
||||
CACHE_FOR_CMAP8 __cache_table[256];
|
||||
enum {
|
||||
kGammaTableSize = 256,
|
||||
kMaxNumberOfPlanes = 3
|
||||
};
|
||||
PFN_dither fDither;
|
||||
PFN_gray fGray;
|
||||
int fPixelDepth;
|
||||
Planes fPlanes;
|
||||
const uchar *fPattern;
|
||||
uint fGammaTable[kGammaTableSize];
|
||||
int fNumberOfPlanes;
|
||||
int fCurrentPlane;
|
||||
// fields used for floyd-steinberg dithering
|
||||
int fX;
|
||||
int fY;
|
||||
int fWidth;
|
||||
int *fErrorTables[kMaxNumberOfPlanes];
|
||||
};
|
||||
|
||||
inline int Halftone::getPixelDepth() const
|
||||
{
|
||||
return __pixel_depth;
|
||||
}
|
||||
|
||||
inline const rgb_color *Halftone::getPalette() const
|
||||
{
|
||||
return __palette;
|
||||
return fPixelDepth;
|
||||
}
|
||||
|
||||
inline const uchar * Halftone::getPattern() const
|
||||
{
|
||||
return __pattern;
|
||||
return fPattern;
|
||||
}
|
||||
|
||||
inline void Halftone::setPattern(const uchar *pattern)
|
||||
{
|
||||
__pattern = pattern;
|
||||
fPattern = pattern;
|
||||
}
|
||||
|
||||
inline PFN_gray Halftone::getGrayFunction() const
|
||||
{
|
||||
return __gray;
|
||||
return fGray;
|
||||
}
|
||||
inline void Halftone::setGrayFunction(PFN_gray gray)
|
||||
{
|
||||
__gray = gray;
|
||||
fGray = gray;
|
||||
}
|
||||
|
||||
inline uint Halftone::getDensity(ColorRGB32 c) const
|
||||
{
|
||||
return fGammaTable[fGray(c)];
|
||||
}
|
||||
|
||||
#endif /* __HALFTONE_H */
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Copyright 1999-2000 Y.Takagi. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <Debug.h>
|
||||
#include <InterfaceDefs.h>
|
||||
#include <math.h>
|
||||
#include <memory>
|
||||
@ -18,78 +19,91 @@ using namespace std;
|
||||
|
||||
#include "Pattern.h"
|
||||
|
||||
static uint gray(rgb_color c)
|
||||
static uint gray(ColorRGB32 c)
|
||||
{
|
||||
if (c.red == c.green && c.red == c.blue) {
|
||||
return 255 - c.red;
|
||||
if (c.little.red == c.little.green && c.little.red == c.little.blue) {
|
||||
return 255 - c.little.red;
|
||||
} else {
|
||||
return 255 - (c.red * 3 + c.green * 6 + c.blue) / 10;
|
||||
return 255 - (c.little.red * 3 + c.little.green * 6 + c.little.blue) / 10;
|
||||
}
|
||||
}
|
||||
|
||||
static uint channel_red(rgb_color c)
|
||||
static uint channel_red(ColorRGB32 c)
|
||||
{
|
||||
return c.red;
|
||||
return c.little.red;
|
||||
}
|
||||
|
||||
static uint channel_green(rgb_color c)
|
||||
static uint channel_green(ColorRGB32 c)
|
||||
{
|
||||
return c.green;
|
||||
return c.little.green;
|
||||
}
|
||||
|
||||
static uint channel_blue(rgb_color c)
|
||||
static uint channel_blue(ColorRGB32 c)
|
||||
{
|
||||
return c.blue;
|
||||
return c.little.blue;
|
||||
}
|
||||
|
||||
Halftone::Halftone(color_space cs, double gamma, DITHERTYPE dither_type)
|
||||
Halftone::Halftone(color_space cs, double gamma, DitherType dither_type)
|
||||
{
|
||||
__pixel_depth = color_space2pixel_depth(cs);
|
||||
__palette = system_colors()->color_list;
|
||||
__gray = gray;
|
||||
|
||||
fPixelDepth = color_space2pixel_depth(cs);
|
||||
fGray = gray;
|
||||
setPlanes(kPlaneMonochrome1);
|
||||
|
||||
initFloydSteinberg();
|
||||
|
||||
createGammaTable(gamma);
|
||||
memset(__cache_table, 0, sizeof(__cache_table));
|
||||
|
||||
if (dither_type == kTypeFloydSteinberg) {
|
||||
fDither = &Halftone::ditherFloydSteinberg;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dither_type) {
|
||||
case TYPE2:
|
||||
__pattern = pattern16x16_type2;
|
||||
case kType2:
|
||||
fPattern = pattern16x16_type2;
|
||||
break;
|
||||
case TYPE3:
|
||||
__pattern = pattern16x16_type3;
|
||||
case kType3:
|
||||
fPattern = pattern16x16_type3;
|
||||
break;
|
||||
default:
|
||||
__pattern = pattern16x16_type1;
|
||||
fPattern = pattern16x16_type1;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cs) {
|
||||
case B_GRAY1:
|
||||
__dither = &Halftone::ditherGRAY1;
|
||||
break;
|
||||
case B_GRAY8:
|
||||
__dither = &Halftone::ditherGRAY8;
|
||||
break;
|
||||
case B_RGB32:
|
||||
case B_RGB32_BIG:
|
||||
__dither = &Halftone::ditherRGB32;
|
||||
fDither = &Halftone::ditherRGB32;
|
||||
break;
|
||||
// case B_CMAP8:
|
||||
default:
|
||||
__dither = &Halftone::ditherCMAP8;
|
||||
fDither = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Halftone::~Halftone()
|
||||
{
|
||||
uninitFloydSteinberg();
|
||||
}
|
||||
|
||||
void Halftone::setPlanes(Planes planes)
|
||||
{
|
||||
fPlanes = planes;
|
||||
if (planes == kPlaneMonochrome1) {
|
||||
fNumberOfPlanes = 1;
|
||||
fGray = gray;
|
||||
} else {
|
||||
ASSERT(planes == kPlaneRGB1);
|
||||
fNumberOfPlanes = 3;
|
||||
}
|
||||
fCurrentPlane = 0;
|
||||
}
|
||||
|
||||
void Halftone::createGammaTable(double gamma)
|
||||
{
|
||||
// gamma = 1.0f / gamma;
|
||||
uint *g = __gamma_table;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint *g = fGammaTable;
|
||||
for (int i = 0; i < kGammaTableSize; i++) {
|
||||
*g++ = (uint)(pow((double)i / 255.0, gamma) * 256.0);
|
||||
}
|
||||
}
|
||||
@ -99,7 +113,7 @@ void Halftone::initElements(int x, int y, uchar *elements)
|
||||
x &= 0x0F;
|
||||
y &= 0x0F;
|
||||
|
||||
const uchar *left = &__pattern[y * 16];
|
||||
const uchar *left = &fPattern[y * 16];
|
||||
const uchar *pos = left + x;
|
||||
const uchar *right = left + 0x0F;
|
||||
|
||||
@ -120,7 +134,32 @@ int Halftone::dither(
|
||||
int y,
|
||||
int width)
|
||||
{
|
||||
return (this->*__dither)(dst, src, x, y, width);
|
||||
int result;
|
||||
|
||||
if (fPlanes == kPlaneRGB1) {
|
||||
switch (fCurrentPlane) {
|
||||
case 0:
|
||||
setGrayFunction(kRedChannel);
|
||||
break;
|
||||
case 1:
|
||||
setGrayFunction(kGreenChannel);
|
||||
break;
|
||||
case 2:
|
||||
setGrayFunction(kBlueChannel);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ASSERT(fGray == &gray);
|
||||
}
|
||||
|
||||
result = (this->*fDither)(dst, src, x, y, width);
|
||||
|
||||
// next plane
|
||||
fCurrentPlane ++;
|
||||
if (fCurrentPlane >= fNumberOfPlanes) {
|
||||
fCurrentPlane = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Halftone::setGrayFunction(GrayFunction grayFunction)
|
||||
@ -139,139 +178,6 @@ void Halftone::setGrayFunction(GrayFunction grayFunction)
|
||||
setGrayFunction(function);
|
||||
}
|
||||
|
||||
|
||||
int Halftone::ditherGRAY1(
|
||||
uchar *dst,
|
||||
const uchar *src,
|
||||
int,
|
||||
int,
|
||||
int width)
|
||||
{
|
||||
int widthByte = (width + 7) / 8;
|
||||
memcpy(dst, src, widthByte);
|
||||
return widthByte;
|
||||
}
|
||||
|
||||
int Halftone::ditherGRAY8(
|
||||
uchar *dst,
|
||||
const uchar *src,
|
||||
int x,
|
||||
int y,
|
||||
int width)
|
||||
{
|
||||
uchar elements[16];
|
||||
initElements(x, y, elements);
|
||||
|
||||
int widthByte = (width + 7) / 8;
|
||||
int remainder = width % 8;
|
||||
if (!remainder)
|
||||
remainder = 8;
|
||||
|
||||
uchar cur;
|
||||
uint density;
|
||||
int i, j;
|
||||
uchar *e = elements;
|
||||
uchar *last_e = elements + 16;
|
||||
|
||||
if (width >= 8) {
|
||||
for (i = 0; i < widthByte - 1; i++) {
|
||||
cur = 0;
|
||||
if (e == last_e) {
|
||||
e = elements;
|
||||
}
|
||||
for (j = 0; j < 8; j++) {
|
||||
density = __gamma_table[*src++];
|
||||
if (density > *e++) {
|
||||
cur |= (0x80 >> j);
|
||||
}
|
||||
}
|
||||
*dst++ = cur;
|
||||
}
|
||||
}
|
||||
if (remainder > 0) {
|
||||
cur = 0;
|
||||
if (e == last_e) {
|
||||
e = elements;
|
||||
}
|
||||
for (j = 0; j < remainder; j++) {
|
||||
density = __gamma_table[*src++];
|
||||
if (density > *e++) {
|
||||
cur |= (0x80 >> j);
|
||||
}
|
||||
}
|
||||
*dst++ = cur;
|
||||
}
|
||||
|
||||
return widthByte;
|
||||
}
|
||||
|
||||
int Halftone::ditherCMAP8(
|
||||
uchar *dst,
|
||||
const uchar *src,
|
||||
int x,
|
||||
int y,
|
||||
int width)
|
||||
{
|
||||
uchar elements[16];
|
||||
initElements(x, y, elements);
|
||||
|
||||
int widthByte = (width + 7) / 8;
|
||||
int remainder = width % 8;
|
||||
if (!remainder)
|
||||
remainder = 8;
|
||||
|
||||
rgb_color c;
|
||||
uchar cur;
|
||||
int i, j;
|
||||
|
||||
uchar *e = elements;
|
||||
uchar *last_e = elements + 16;
|
||||
CACHE_FOR_CMAP8 *cache;
|
||||
|
||||
if (width >= 8) {
|
||||
for (i = 0; i < widthByte - 1; i++) {
|
||||
cur = 0;
|
||||
if (e == last_e) {
|
||||
e = elements;
|
||||
}
|
||||
for (j = 0; j < 8; j++) {
|
||||
cache = &__cache_table[*src];
|
||||
if (!cache->hit) {
|
||||
cache->hit = true;
|
||||
c = __palette[*src];
|
||||
cache->density = __gamma_table[__gray(c)];
|
||||
}
|
||||
src++;
|
||||
if (cache->density > *e++) {
|
||||
cur |= (0x80 >> j);
|
||||
}
|
||||
}
|
||||
*dst++ = cur;
|
||||
}
|
||||
}
|
||||
if (remainder > 0) {
|
||||
cur = 0;
|
||||
if (e == last_e) {
|
||||
e = elements;
|
||||
}
|
||||
for (j = 0; j < remainder; j++) {
|
||||
cache = &__cache_table[*src];
|
||||
if (!cache->hit) {
|
||||
cache->hit = true;
|
||||
c = __palette[*src];
|
||||
cache->density = __gamma_table[__gray(c)];
|
||||
}
|
||||
src++;
|
||||
if (cache->density > *e++) {
|
||||
cur |= (0x80 >> j);
|
||||
}
|
||||
}
|
||||
*dst++ = cur;
|
||||
}
|
||||
|
||||
return widthByte;
|
||||
}
|
||||
|
||||
int Halftone::ditherRGB32(
|
||||
uchar *dst,
|
||||
const uchar *a_src,
|
||||
@ -282,14 +188,14 @@ int Halftone::ditherRGB32(
|
||||
uchar elements[16];
|
||||
initElements(x, y, elements);
|
||||
|
||||
const rgb_color *src = (const rgb_color *)a_src;
|
||||
const ColorRGB32 *src = (const ColorRGB32 *)a_src;
|
||||
|
||||
int widthByte = (width + 7) / 8;
|
||||
int remainder = width % 8;
|
||||
if (remainder == 0)
|
||||
remainder = 8;
|
||||
|
||||
rgb_color c;
|
||||
ColorRGB32 c;
|
||||
uchar cur;
|
||||
uint density;
|
||||
int i, j;
|
||||
@ -297,7 +203,7 @@ int Halftone::ditherRGB32(
|
||||
uchar *last_e = elements + 16;
|
||||
|
||||
c = *src;
|
||||
density = __gamma_table[__gray(c)];
|
||||
density = getDensity(c);
|
||||
|
||||
if (width >= 8) {
|
||||
for (i = 0; i < widthByte - 1; i++) {
|
||||
@ -306,9 +212,9 @@ int Halftone::ditherRGB32(
|
||||
e = elements;
|
||||
}
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (c.red != src->red || c.green != src->green || c.blue != src->blue) {
|
||||
if (c.little.red != src->little.red || c.little.green != src->little.green || c.little.blue != src->little.blue) {
|
||||
c = *src;
|
||||
density = __gamma_table[__gray(c)];
|
||||
density = getDensity(c);
|
||||
}
|
||||
src++;
|
||||
if (density > *e++) {
|
||||
@ -324,9 +230,9 @@ int Halftone::ditherRGB32(
|
||||
e = elements;
|
||||
}
|
||||
for (j = 0; j < remainder; j++) {
|
||||
if (c.red != src->red || c.green != src->green || c.blue != src->blue) {
|
||||
if (c.little.red != src->little.red || c.little.green != src->little.green || c.little.blue != src->little.blue) {
|
||||
c = *src;
|
||||
density = __gamma_table[__gray(c)];
|
||||
density = getDensity(c);
|
||||
}
|
||||
src++;
|
||||
if (density > *e++) {
|
||||
@ -338,3 +244,86 @@ int Halftone::ditherRGB32(
|
||||
|
||||
return widthByte;
|
||||
}
|
||||
|
||||
// Floyd-Steinberg dithering
|
||||
void Halftone::initFloydSteinberg()
|
||||
{
|
||||
for (int i = 0; i < kMaxNumberOfPlanes; i ++) {
|
||||
fErrorTables[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Halftone::deleteErrorTables()
|
||||
{
|
||||
for (int i = 0; i < kMaxNumberOfPlanes; i ++) {
|
||||
delete fErrorTables[i];
|
||||
fErrorTables[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Halftone::uninitFloydSteinberg()
|
||||
{
|
||||
deleteErrorTables();
|
||||
}
|
||||
|
||||
void Halftone::setupErrorBuffer(int x, int y, int width)
|
||||
{
|
||||
deleteErrorTables();
|
||||
fX = x;
|
||||
fY = y;
|
||||
fWidth = width;
|
||||
for (int i = 0; i < fNumberOfPlanes; i ++) {
|
||||
// reserve also space for sentinals at both ends of error table
|
||||
const int size = width + 2;
|
||||
fErrorTables[i] = new int[size];
|
||||
memset(fErrorTables[i], 0, sizeof(int) * size);
|
||||
}
|
||||
}
|
||||
|
||||
int Halftone::ditherFloydSteinberg(uchar *dst, const uchar* a_src, int x, int y, int width)
|
||||
{
|
||||
if (fErrorTables[fCurrentPlane] == NULL || fX != x || fCurrentPlane == 0 && fY != y - 1 || fCurrentPlane > 0 && fY != y || fWidth != width) {
|
||||
setupErrorBuffer(x, y, width);
|
||||
} else {
|
||||
fY = y;
|
||||
}
|
||||
|
||||
int* error_table = &fErrorTables[fCurrentPlane][1];
|
||||
int current_error = 0, error;
|
||||
const ColorRGB32 *src = (const ColorRGB32 *)a_src;
|
||||
for (int x = 0; x < width; x ++, src ++) {
|
||||
const int bit = 7 - x % 8;
|
||||
if (bit == 7) {
|
||||
// clear byte if at first pixel in byte
|
||||
*dst = 0;
|
||||
}
|
||||
|
||||
int density = getDensity(*src) + current_error / 16;
|
||||
|
||||
if (density < 128) {
|
||||
// white pixel
|
||||
error = density;
|
||||
} else {
|
||||
// black pixel
|
||||
error = density - 255;
|
||||
*dst |= (1 << bit);
|
||||
}
|
||||
|
||||
// distribute error
|
||||
int* right = &error_table[x+1];
|
||||
current_error = *right + 7 * error;
|
||||
*right = error;
|
||||
|
||||
int* middle = right - 1;
|
||||
*middle += 5 * error;
|
||||
|
||||
int* left = middle - 1;
|
||||
*left += 3 * error;
|
||||
|
||||
if (bit == 0) {
|
||||
// advance to next byte
|
||||
dst ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user