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