xref: /haiku/src/libs/print/libprint/Halftone.cpp (revision 26db1794f9199838b3d4c8e6199f72dcf9cfec08)
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