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
ToGray(ColorRGB32 c)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
GetRedValue(ColorRGB32 c)32 GetRedValue(ColorRGB32 c)
33 {
34 return c.little.red;
35 }
36
37
38 static uint
GetGreenValue(ColorRGB32 c)39 GetGreenValue(ColorRGB32 c)
40 {
41 return c.little.green;
42 }
43
44
45 static uint
GetBlueValue(ColorRGB32 c)46 GetBlueValue(ColorRGB32 c)
47 {
48 return c.little.blue;
49 }
50
51
Halftone(color_space colorSpace,double gamma,double min,DitherType ditherType)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
~Halftone()93 Halftone::~Halftone()
94 {
95 UninitFloydSteinberg();
96 }
97
98
99 void
SetPlanes(Planes planes)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
SetBlackValue(BlackValue blackValue)115 Halftone::SetBlackValue(BlackValue blackValue)
116 {
117 fBlackValue = blackValue;
118 }
119
120
121 void
CreateGammaTable(double gamma,double min)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
InitElements(int x,int y,uchar * elements)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
Dither(uchar * destination,const uchar * source,int x,int y,int width)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
SetGrayFunction(GrayFunction grayFunction)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
DitherRGB32(uchar * destination,const uchar * source0,int x,int y,int width)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
InitFloydSteinberg()270 Halftone::InitFloydSteinberg()
271 {
272 for (int i = 0; i < kMaxNumberOfPlanes; i ++)
273 fErrorTables[i] = NULL;
274 }
275
276
277 void
DeleteErrorTables()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
UninitFloydSteinberg()288 Halftone::UninitFloydSteinberg()
289 {
290 DeleteErrorTables();
291 }
292
293
294 void
SetupErrorBuffer(int x,int y,int width)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
DitherFloydSteinberg(uchar * destination,const uchar * source0,int x,int y,int width)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