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:
Michael Pfeiffer 2004-01-30 17:09:32 +00:00
parent d7f45f5855
commit 7ea239b20d
2 changed files with 231 additions and 205 deletions

View File

@ -8,21 +8,38 @@
#include <GraphicsDefs.h> #include <GraphicsDefs.h>
struct CACHE_FOR_CMAP8 { // definition for B_RGB32 (=B_RGB32_LITTLE) and B_RGBA32
uint density; typedef struct {
bool hit; 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; class Halftone;
typedef int (Halftone::*PFN_dither)(uchar *dst, const uchar *src, int x, int y, int width); 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 { class Halftone {
public: public:
enum DITHERTYPE { enum DitherType {
TYPE1, kType1,
TYPE2, kType2,
TYPE3 kType3,
kTypeFloydSteinberg,
}; };
enum GrayFunction { enum GrayFunction {
kMixToGray, kMixToGray,
@ -30,65 +47,85 @@ public:
kGreenChannel, kGreenChannel,
kBlueChannel 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(); ~Halftone();
void setPlanes(Planes planes);
int dither(uchar *dst, const uchar *src, int x, int y, int width); int dither(uchar *dst, const uchar *src, int x, int y, int width);
int getPixelDepth() const; int getPixelDepth() const;
const rgb_color *getPalette() const; const rgb_color *getPalette() const;
const uchar *getPattern() const; const uchar *getPattern() const;
void setPattern(const uchar *pattern); void setPattern(const uchar *pattern);
protected:
PFN_gray getGrayFunction() const; PFN_gray getGrayFunction() const;
void setGrayFunction(PFN_gray gray); void setGrayFunction(PFN_gray gray);
void setGrayFunction(GrayFunction grayFunction); void setGrayFunction(GrayFunction grayFunction);
protected:
void createGammaTable(double gamma); void createGammaTable(double gamma);
void initElements(int x, int y, uchar *elements); void initElements(int x, int y, uchar *elements);
int ditherGRAY1(uchar *dst, const uchar *src, int x, int y, int width); uint getDensity(ColorRGB32 c) const;
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);
int ditherRGB32(uchar *dst, const uchar *src, int x, int y, int width); 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(const Halftone &);
Halftone &operator = (const Halftone &); Halftone &operator = (const Halftone &);
private: private:
PFN_dither __dither; enum {
PFN_gray __gray; kGammaTableSize = 256,
int __pixel_depth; kMaxNumberOfPlanes = 3
const rgb_color *__palette; };
const uchar *__pattern; PFN_dither fDither;
uint __gamma_table[256]; PFN_gray fGray;
CACHE_FOR_CMAP8 __cache_table[256]; 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 inline int Halftone::getPixelDepth() const
{ {
return __pixel_depth; return fPixelDepth;
}
inline const rgb_color *Halftone::getPalette() const
{
return __palette;
} }
inline const uchar * Halftone::getPattern() const inline const uchar * Halftone::getPattern() const
{ {
return __pattern; return fPattern;
} }
inline void Halftone::setPattern(const uchar *pattern) inline void Halftone::setPattern(const uchar *pattern)
{ {
__pattern = pattern; fPattern = pattern;
} }
inline PFN_gray Halftone::getGrayFunction() const inline PFN_gray Halftone::getGrayFunction() const
{ {
return __gray; return fGray;
} }
inline void Halftone::setGrayFunction(PFN_gray gray) 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 */ #endif /* __HALFTONE_H */

View File

@ -3,6 +3,7 @@
* Copyright 1999-2000 Y.Takagi. All Rights Reserved. * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
*/ */
#include <Debug.h>
#include <InterfaceDefs.h> #include <InterfaceDefs.h>
#include <math.h> #include <math.h>
#include <memory> #include <memory>
@ -18,78 +19,91 @@ using namespace std;
#include "Pattern.h" #include "Pattern.h"
static uint gray(rgb_color c) static uint gray(ColorRGB32 c)
{ {
if (c.red == c.green && c.red == c.blue) { if (c.little.red == c.little.green && c.little.red == c.little.blue) {
return 255 - c.red; return 255 - c.little.red;
} else { } 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); fPixelDepth = color_space2pixel_depth(cs);
__palette = system_colors()->color_list; fGray = gray;
__gray = gray; setPlanes(kPlaneMonochrome1);
initFloydSteinberg();
createGammaTable(gamma); createGammaTable(gamma);
memset(__cache_table, 0, sizeof(__cache_table));
if (dither_type == kTypeFloydSteinberg) {
fDither = &Halftone::ditherFloydSteinberg;
return;
}
switch (dither_type) { switch (dither_type) {
case TYPE2: case kType2:
__pattern = pattern16x16_type2; fPattern = pattern16x16_type2;
break; break;
case TYPE3: case kType3:
__pattern = pattern16x16_type3; fPattern = pattern16x16_type3;
break; break;
default: default:
__pattern = pattern16x16_type1; fPattern = pattern16x16_type1;
break; break;
} }
switch (cs) { switch (cs) {
case B_GRAY1:
__dither = &Halftone::ditherGRAY1;
break;
case B_GRAY8:
__dither = &Halftone::ditherGRAY8;
break;
case B_RGB32: case B_RGB32:
case B_RGB32_BIG: case B_RGB32_BIG:
__dither = &Halftone::ditherRGB32; fDither = &Halftone::ditherRGB32;
break; break;
// case B_CMAP8:
default: default:
__dither = &Halftone::ditherCMAP8; fDither = NULL;
break; break;
} }
} }
Halftone::~Halftone() 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) void Halftone::createGammaTable(double gamma)
{ {
// gamma = 1.0f / gamma; // gamma = 1.0f / gamma;
uint *g = __gamma_table; uint *g = fGammaTable;
for (int i = 0; i < 256; i++) { for (int i = 0; i < kGammaTableSize; i++) {
*g++ = (uint)(pow((double)i / 255.0, gamma) * 256.0); *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; x &= 0x0F;
y &= 0x0F; y &= 0x0F;
const uchar *left = &__pattern[y * 16]; const uchar *left = &fPattern[y * 16];
const uchar *pos = left + x; const uchar *pos = left + x;
const uchar *right = left + 0x0F; const uchar *right = left + 0x0F;
@ -120,7 +134,32 @@ int Halftone::dither(
int y, int y,
int width) 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) void Halftone::setGrayFunction(GrayFunction grayFunction)
@ -139,139 +178,6 @@ void Halftone::setGrayFunction(GrayFunction grayFunction)
setGrayFunction(function); 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( int Halftone::ditherRGB32(
uchar *dst, uchar *dst,
const uchar *a_src, const uchar *a_src,
@ -282,14 +188,14 @@ int Halftone::ditherRGB32(
uchar elements[16]; uchar elements[16];
initElements(x, y, elements); 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 widthByte = (width + 7) / 8;
int remainder = width % 8; int remainder = width % 8;
if (remainder == 0) if (remainder == 0)
remainder = 8; remainder = 8;
rgb_color c; ColorRGB32 c;
uchar cur; uchar cur;
uint density; uint density;
int i, j; int i, j;
@ -297,7 +203,7 @@ int Halftone::ditherRGB32(
uchar *last_e = elements + 16; uchar *last_e = elements + 16;
c = *src; c = *src;
density = __gamma_table[__gray(c)]; density = getDensity(c);
if (width >= 8) { if (width >= 8) {
for (i = 0; i < widthByte - 1; i++) { for (i = 0; i < widthByte - 1; i++) {
@ -306,9 +212,9 @@ int Halftone::ditherRGB32(
e = elements; e = elements;
} }
for (j = 0; j < 8; j++) { 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; c = *src;
density = __gamma_table[__gray(c)]; density = getDensity(c);
} }
src++; src++;
if (density > *e++) { if (density > *e++) {
@ -324,9 +230,9 @@ int Halftone::ditherRGB32(
e = elements; e = elements;
} }
for (j = 0; j < remainder; j++) { 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; c = *src;
density = __gamma_table[__gray(c)]; density = getDensity(c);
} }
src++; src++;
if (density > *e++) { if (density > *e++) {
@ -338,3 +244,86 @@ int Halftone::ditherRGB32(
return widthByte; 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 ++;
}
}
}