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