vfs: check userland buffers before reading them.
[haiku.git] / src / libs / print / libprint / Halftone.cpp
blob5bd385755f109a4e0cf60e3953b2a6390b0fa119
1 /*
2 * Halftone.cpp
3 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
4 */
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"
16 using namespace std;
19 #include "Pattern.h"
22 static uint
23 ToGray(ColorRGB32 c)
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;
31 static uint
32 GetRedValue(ColorRGB32 c)
34 return c.little.red;
38 static uint
39 GetGreenValue(ColorRGB32 c)
41 return c.little.green;
45 static uint
46 GetBlueValue(ColorRGB32 c)
48 return c.little.blue;
52 Halftone::Halftone(color_space colorSpace, double gamma, double min,
53 DitherType ditherType)
55 fPixelDepth = color_space2pixel_depth(colorSpace);
56 fGray = ToGray;
57 SetPlanes(kPlaneMonochrome1);
58 SetBlackValue(kHighValueMeansBlack);
60 InitFloydSteinberg();
62 CreateGammaTable(gamma, min);
64 if (ditherType == kTypeFloydSteinberg) {
65 fDither = &Halftone::DitherFloydSteinberg;
66 return;
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;
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;
93 Halftone::~Halftone()
95 UninitFloydSteinberg();
99 void
100 Halftone::SetPlanes(Planes planes)
102 fPlanes = planes;
103 if (planes == kPlaneMonochrome1) {
104 fNumberOfPlanes = 1;
105 fGray = ToGray;
106 } else {
107 ASSERT(planes == kPlaneRGB1);
108 fNumberOfPlanes = 3;
110 fCurrentPlane = 0;
114 void
115 Halftone::SetBlackValue(BlackValue blackValue)
117 fBlackValue = blackValue;
121 void
122 Halftone::CreateGammaTable(double gamma, double min)
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);
133 void
134 Halftone::InitElements(int x, int y, uchar* elements)
136 x &= 0x0F;
137 y &= 0x0F;
139 const uchar *left = &fPattern[y * 16];
140 const uchar *pos = left + x;
141 const uchar *right = left + 0x0F;
143 for (int i = 0; i < 16; i++) {
144 elements[i] = *pos;
145 if (pos >= right)
146 pos = left;
147 else
148 pos++;
153 void
154 Halftone::Dither(uchar* destination, const uchar* source, int x, int y,
155 int width)
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;
169 } else {
170 ASSERT(fGray == &ToGray);
173 (this->*fDither)(destination, source, x, y, width);
175 // next plane
176 fCurrentPlane ++;
177 if (fCurrentPlane >= fNumberOfPlanes)
178 fCurrentPlane = 0;
182 void
183 Halftone::SetGrayFunction(GrayFunction grayFunction)
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;
196 SetGrayFunction(function);
200 void
201 Halftone::DitherRGB32(uchar *destination, const uchar *source0, int x, int y,
202 int width)
204 uchar elements[16];
205 InitElements(x, y, elements);
207 const ColorRGB32* source = reinterpret_cast<const ColorRGB32*>(source0);
209 int widthByte = (width + 7) / 8;
210 int remainder = width % 8;
211 if (remainder == 0)
212 remainder = 8;
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;
221 c = *source;
222 density = GetDensity(c);
224 if (width >= 8) {
225 for (i = 0; i < widthByte - 1; i++) {
226 cur = 0;
227 if (e == last_e) {
228 e = elements;
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);
237 source++;
238 if (density <= *e++) {
239 cur |= (0x80 >> j);
242 *destination++ = ConvertUsingBlackValue(cur);
246 if (remainder > 0) {
247 cur = 0;
248 if (e == last_e) {
249 e = elements;
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);
258 source++;
259 if (density <= *e++) {
260 cur |= (0x80 >> j);
263 *destination++ = ConvertUsingBlackValue(cur);
268 // Floyd-Steinberg dithering
269 void
270 Halftone::InitFloydSteinberg()
272 for (int i = 0; i < kMaxNumberOfPlanes; i ++)
273 fErrorTables[i] = NULL;
277 void
278 Halftone::DeleteErrorTables()
280 for (int i = 0; i < kMaxNumberOfPlanes; i ++) {
281 delete fErrorTables[i];
282 fErrorTables[i] = NULL;
287 void
288 Halftone::UninitFloydSteinberg()
290 DeleteErrorTables();
294 void
295 Halftone::SetupErrorBuffer(int x, int y, int width)
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);
310 void
311 Halftone::DitherFloydSteinberg(uchar *destination, const uchar* source0,
312 int x, int y, int width)
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;
322 int* errorTable = &fErrorTables[fCurrentPlane][1];
323 int current_error = errorTable[0];
324 int error;
325 errorTable[0] = 0;
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;
333 if (density < 128) {
334 error = density;
335 cur |= (1 << bit);
336 } else {
337 error = density - 255;
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;
348 int* middle = right - 1;
349 *middle += 5 * error;
351 int* left = middle - 1;
352 *left += 3 * error;
354 if (bit == 0) {
355 *destination = ConvertUsingBlackValue(cur);
356 // advance to next byte
357 destination ++;
358 cur = 0;
362 const bool hasRest = (width % 8) != 0;
363 if (hasRest) {
364 *destination = ConvertUsingBlackValue(cur);