1 /* 2 * Halftone.cpp 3 * Copyright 1999-2000 Y.Takagi. All Rights Reserved. 4 */ 5 6 #include <Debug.h> 7 #include <InterfaceDefs.h> 8 #include <math.h> 9 #include <memory> 10 #include "Halftone.h" 11 #include "ValidRect.h" 12 #include "DbgMsg.h" 13 14 #if (!__MWERKS__ || defined(MSIPL_USING_NAMESPACE)) 15 using namespace std; 16 #else 17 #define std 18 #endif 19 20 #include "Pattern.h" 21 22 static uint gray(ColorRGB32 c) 23 { 24 if (c.little.red == c.little.green && c.little.red == c.little.blue) { 25 return c.little.red; 26 } else { 27 return (c.little.red * 3 + c.little.green * 6 + c.little.blue) / 10; 28 } 29 } 30 31 static uint channel_red(ColorRGB32 c) 32 { 33 return c.little.red; 34 } 35 36 static uint channel_green(ColorRGB32 c) 37 { 38 return c.little.green; 39 } 40 41 static uint channel_blue(ColorRGB32 c) 42 { 43 return c.little.blue; 44 } 45 46 Halftone::Halftone(color_space cs, double gamma, double min, DitherType dither_type) 47 { 48 fPixelDepth = color_space2pixel_depth(cs); 49 fGray = gray; 50 setPlanes(kPlaneMonochrome1); 51 setBlackValue(kHighValueMeansBlack); 52 53 initFloydSteinberg(); 54 55 createGammaTable(gamma, min); 56 57 if (dither_type == kTypeFloydSteinberg) { 58 fDither = &Halftone::ditherFloydSteinberg; 59 return; 60 } 61 62 switch (dither_type) { 63 case kType2: 64 fPattern = pattern16x16_type2; 65 break; 66 case kType3: 67 fPattern = pattern16x16_type3; 68 break; 69 default: 70 fPattern = pattern16x16_type1; 71 break; 72 } 73 74 switch (cs) { 75 case B_RGB32: 76 case B_RGB32_BIG: 77 fDither = &Halftone::ditherRGB32; 78 break; 79 default: 80 fDither = NULL; 81 break; 82 } 83 } 84 85 Halftone::~Halftone() 86 { 87 uninitFloydSteinberg(); 88 } 89 90 void Halftone::setPlanes(Planes planes) 91 { 92 fPlanes = planes; 93 if (planes == kPlaneMonochrome1) { 94 fNumberOfPlanes = 1; 95 fGray = gray; 96 } else { 97 ASSERT(planes == kPlaneRGB1); 98 fNumberOfPlanes = 3; 99 } 100 fCurrentPlane = 0; 101 } 102 103 void Halftone::setBlackValue(BlackValue blackValue) 104 { 105 fBlackValue = blackValue; 106 } 107 108 void Halftone::createGammaTable(double gamma, double min) 109 { 110 uint *g = fGammaTable; 111 const double kScalingFactor = 255.0 - min; 112 for (int i = 0; i < kGammaTableSize; i++) { 113 const double kGammaCorrectedValue = pow((double)i / 255.0, gamma); 114 const double kTranslatedValue = min + kGammaCorrectedValue * kScalingFactor; 115 *g++ = (uint)(kTranslatedValue); 116 } 117 } 118 119 void Halftone::initElements(int x, int y, uchar *elements) 120 { 121 x &= 0x0F; 122 y &= 0x0F; 123 124 const uchar *left = &fPattern[y * 16]; 125 const uchar *pos = left + x; 126 const uchar *right = left + 0x0F; 127 128 for (int i = 0; i < 16; i++) { 129 *elements++ = *pos; 130 if (pos >= right) { 131 pos = left; 132 } else { 133 pos++; 134 } 135 } 136 } 137 138 void Halftone::dither( 139 uchar *dst, 140 const uchar *src, 141 int x, 142 int y, 143 int width) 144 { 145 if (fPlanes == kPlaneRGB1) { 146 switch (fCurrentPlane) { 147 case 0: 148 setGrayFunction(kRedChannel); 149 break; 150 case 1: 151 setGrayFunction(kGreenChannel); 152 break; 153 case 2: 154 setGrayFunction(kBlueChannel); 155 break; 156 } 157 } else { 158 ASSERT(fGray == &gray); 159 } 160 161 (this->*fDither)(dst, src, x, y, width); 162 163 // next plane 164 fCurrentPlane ++; 165 if (fCurrentPlane >= fNumberOfPlanes) { 166 fCurrentPlane = 0; 167 } 168 } 169 170 void Halftone::setGrayFunction(GrayFunction grayFunction) 171 { 172 PFN_gray function = NULL; 173 switch (grayFunction) { 174 case kMixToGray: function = gray; 175 break; 176 case kRedChannel: function = channel_red; 177 break; 178 case kGreenChannel: function = channel_green; 179 break; 180 case kBlueChannel: function = channel_blue; 181 break; 182 }; 183 setGrayFunction(function); 184 } 185 186 void Halftone::ditherRGB32( 187 uchar *dst, 188 const uchar *a_src, 189 int x, 190 int y, 191 int width) 192 { 193 uchar elements[16]; 194 initElements(x, y, elements); 195 196 const ColorRGB32 *src = (const ColorRGB32 *)a_src; 197 198 int widthByte = (width + 7) / 8; 199 int remainder = width % 8; 200 if (remainder == 0) 201 remainder = 8; 202 203 ColorRGB32 c; 204 uchar cur; // cleared bit means white, set bit means black 205 uint density; 206 int i, j; 207 uchar *e = elements; 208 uchar *last_e = elements + 16; 209 210 c = *src; 211 density = getDensity(c); 212 213 if (width >= 8) { 214 for (i = 0; i < widthByte - 1; i++) { 215 cur = 0; 216 if (e == last_e) { 217 e = elements; 218 } 219 for (j = 0; j < 8; j++) { 220 if (c.little.red != src->little.red || c.little.green != src->little.green || c.little.blue != src->little.blue) { 221 c = *src; 222 density = getDensity(c); 223 } 224 src++; 225 if (density <= *e++) { 226 cur |= (0x80 >> j); 227 } 228 } 229 *dst++ = convertUsingBlackValue(cur); 230 } 231 } 232 if (remainder > 0) { 233 cur = 0; 234 if (e == last_e) { 235 e = elements; 236 } 237 for (j = 0; j < remainder; j++) { 238 if (c.little.red != src->little.red || c.little.green != src->little.green || c.little.blue != src->little.blue) { 239 c = *src; 240 density = getDensity(c); 241 } 242 src++; 243 if (density <= *e++) { 244 cur |= (0x80 >> j); 245 } 246 } 247 *dst++ = convertUsingBlackValue(cur); 248 } 249 } 250 251 // Floyd-Steinberg dithering 252 void Halftone::initFloydSteinberg() 253 { 254 for (int i = 0; i < kMaxNumberOfPlanes; i ++) { 255 fErrorTables[i] = NULL; 256 } 257 } 258 259 void Halftone::deleteErrorTables() 260 { 261 for (int i = 0; i < kMaxNumberOfPlanes; i ++) { 262 delete fErrorTables[i]; 263 fErrorTables[i] = NULL; 264 } 265 } 266 267 void Halftone::uninitFloydSteinberg() 268 { 269 deleteErrorTables(); 270 } 271 272 void Halftone::setupErrorBuffer(int x, int y, int width) 273 { 274 deleteErrorTables(); 275 fX = x; 276 fY = y; 277 fWidth = width; 278 for (int i = 0; i < fNumberOfPlanes; i ++) { 279 // reserve also space for sentinals at both ends of error table 280 const int size = width + 2; 281 fErrorTables[i] = new int[size]; 282 memset(fErrorTables[i], 0, sizeof(int) * size); 283 } 284 } 285 286 void Halftone::ditherFloydSteinberg(uchar *dst, const uchar* a_src, int x, int y, int width) 287 { 288 if (fErrorTables[fCurrentPlane] == NULL || fX != x || fCurrentPlane == 0 && fY != y - 1 || fCurrentPlane > 0 && fY != y || fWidth != width) { 289 setupErrorBuffer(x, y, width); 290 } else { 291 fY = y; 292 } 293 294 int* error_table = &fErrorTables[fCurrentPlane][1]; 295 int current_error = error_table[0], error; 296 error_table[0] = 0; 297 const ColorRGB32 *src = (const ColorRGB32 *)a_src; 298 uchar cur = 0; // cleared bit means white, set bit means black 299 for (int x = 0; x < width; x ++, src ++) { 300 const int bit = 7 - x % 8; 301 const int density = getDensity(*src) + current_error / 16; 302 303 if (density < 128) { 304 error = density; 305 cur |= (1 << bit); 306 } else { 307 error = density - 255; 308 } 309 310 // distribute error using this pattern: 311 // 0 X 7 (current_error) 312 // (left) 3 5 1 (right) 313 // (middle) 314 int* right = &error_table[x+1]; 315 current_error = (*right) + 7 * error; 316 *right = 1 * error; 317 318 int* middle = right - 1; 319 *middle += 5 * error; 320 321 int* left = middle - 1; 322 *left += 3 * error; 323 324 if (bit == 0) { 325 *dst = convertUsingBlackValue(cur); 326 // advance to next byte 327 dst ++; 328 cur = 0; 329 } 330 } 331 332 const bool hasRest = (width % 8) != 0; 333 if (hasRest) { 334 *dst = convertUsingBlackValue(cur); 335 } 336 } 337 338