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