From 7ea239b20d26aece9b304d6bd4261250557e4c99 Mon Sep 17 00:00:00 2001 From: Michael Pfeiffer Date: Fri, 30 Jan 2004 17:09:32 +0000 Subject: [PATCH] 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 --- headers/private/print/libprint/Halftone.h | 99 +++-- .../drivers/shared/libprint/Halftone.cpp | 337 +++++++++--------- 2 files changed, 231 insertions(+), 205 deletions(-) diff --git a/headers/private/print/libprint/Halftone.h b/headers/private/print/libprint/Halftone.h index 95c23ac4de..a3c71e3720 100644 --- a/headers/private/print/libprint/Halftone.h +++ b/headers/private/print/libprint/Halftone.h @@ -8,21 +8,38 @@ #include -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 */ diff --git a/src/add-ons/print/drivers/shared/libprint/Halftone.cpp b/src/add-ons/print/drivers/shared/libprint/Halftone.cpp index ed0928fa5d..ce0568e3af 100644 --- a/src/add-ons/print/drivers/shared/libprint/Halftone.cpp +++ b/src/add-ons/print/drivers/shared/libprint/Halftone.cpp @@ -3,6 +3,7 @@ * Copyright 1999-2000 Y.Takagi. All Rights Reserved. */ +#include #include #include #include @@ -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 ++; + } + } +} +