Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(433)

Side by Side Diff: src/pdf/SkPDFUtils.cpp

Issue 2146103004: SkPDF: re-work SkPDFUtils::FloatToDecimal (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: 2016-07-14 (Thursday) 15:50:08 EDT Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/PDFPrimitivesTest.cpp » ('j') | tests/PDFPrimitivesTest.cpp » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2011 Google Inc. 2 * Copyright 2011 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 8
9 #include "SkData.h" 9 #include "SkData.h"
10 #include "SkGeometry.h" 10 #include "SkGeometry.h"
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after
251 content->writeText(" scn\n"); 251 content->writeText(" scn\n");
252 } 252 }
253 253
254 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) { 254 void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) {
255 char result[kMaximumFloatDecimalLength]; 255 char result[kMaximumFloatDecimalLength];
256 size_t len = SkPDFUtils::FloatToDecimal(SkScalarToFloat(value), result); 256 size_t len = SkPDFUtils::FloatToDecimal(SkScalarToFloat(value), result);
257 SkASSERT(len < kMaximumFloatDecimalLength); 257 SkASSERT(len < kMaximumFloatDecimalLength);
258 stream->write(result, len); 258 stream->write(result, len);
259 } 259 }
260 260
261 // Return pow(10.0, e), optimized for common cases.
tomhudson 2016/07/15 20:45:41 Huh, this is really faster?
hal.canary 2016/07/17 02:33:42 yep.
262 inline double pow10(int e) {
263 switch (e) {
264 case 0: return 1.0; // common cases
265 case 1: return 10.0;
266 case 2: return 100.0;
267 case 3: return 1e+03;
268 case 4: return 1e+04;
269 case 5: return 1e+05;
270 case 6: return 1e+06;
271 case 7: return 1e+07;
272 case 8: return 1e+08;
273 case 9: return 1e+09;
274 case 10: return 1e+10;
275 case 11: return 1e+11;
276 case 12: return 1e+12;
277 case 13: return 1e+13;
278 case 14: return 1e+14;
279 case 15: return 1e+15;
280 default:
281 if (e > 15) {
282 double value = 1e+15;
283 while (e-- > 15) { value *= 10.0; }
284 return value;
285 } else {
286 SkASSERT(e < 0);
287 double value = 1.0;
288 while (e++ < 0) { value /= 10.0; }
289 return value;
290 }
291 }
292 }
293
261 /** Write a string into result, includeing a terminating '\0' (for 294 /** Write a string into result, includeing a terminating '\0' (for
262 unit testing). Return strlen(result) (for SkWStream::write) The 295 unit testing). Return strlen(result) (for SkWStream::write) The
263 resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and 296 resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
264 sscanf(result, "%f", &x) will return the original value iff the 297 sscanf(result, "%f", &x) will return the original value iff the
265 value is finite. This function accepts all possible input values. 298 value is finite. This function accepts all possible input values.
266 299
267 Motivation: "PDF does not support [numbers] in exponential format 300 Motivation: "PDF does not support [numbers] in exponential format
268 (such as 6.02e23)." Otherwise, this function would rely on a 301 (such as 6.02e23)." Otherwise, this function would rely on a
269 sprintf-type function from the standard library. */ 302 sprintf-type function from the standard library. */
270 size_t SkPDFUtils::FloatToDecimal(float value, 303 size_t SkPDFUtils::FloatToDecimal(float value,
(...skipping 25 matching lines...) Expand all
296 if (value == SK_FloatNegativeInfinity) { 329 if (value == SK_FloatNegativeInfinity) {
297 value = -FLT_MAX; // nearest finite float. 330 value = -FLT_MAX; // nearest finite float.
298 } 331 }
299 if (!std::isfinite(value) || value == 0.0f) { 332 if (!std::isfinite(value) || value == 0.0f) {
300 // NAN is unsupported in PDF. Always output a valid number. 333 // NAN is unsupported in PDF. Always output a valid number.
301 // Also catch zero here, as a special case. 334 // Also catch zero here, as a special case.
302 *output++ = '0'; 335 *output++ = '0';
303 *output = '\0'; 336 *output = '\0';
304 return output - result; 337 return output - result;
305 } 338 }
306 // Inspired by:
307 // http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal- conversion/
308
309 if (value < 0.0) { 339 if (value < 0.0) {
310 *output++ = '-'; 340 *output++ = '-';
311 value = -value; 341 value = -value;
312 } 342 }
313 SkASSERT(value >= 0.0f); 343 SkASSERT(value >= 0.0f);
314 344
315 // Must use double math to keep precision right. 345 int binaryExponents;
tomhudson 2016/07/15 20:45:41 Bikeshed: Why is this name plural?
hal.canary 2016/07/17 02:33:42 Done.
316 double intPart; 346 (void)frexp(value, &binaryExponents);
tomhudson 2016/07/15 20:45:41 std::frexp()? Or is that not our style?
hal.canary 2016/07/17 02:33:42 We do it both ways. the C++ish way seems to be ga
317 double fracPart = std::modf(static_cast<double>(value), &intPart); 347 static const double kLog2 = 0.3010299956639812; // log10(2.0);
tomhudson 2016/07/15 20:45:41 constexpr?
hal.canary 2016/07/17 02:33:42 std::log10 isn't constexpr.
318 SkASSERT(intPart + fracPart == static_cast<double>(value)); 348 int decimalExponents = (int)floor(kLog2 * binaryExponents);
tomhudson 2016/07/15 20:45:41 Bikeshed as above
hal.canary 2016/07/17 02:33:42 Done.
319 size_t significantDigits = 0; 349 int decShift = decimalExponents - 8;
320 const size_t maxSignificantDigits = 9; 350 double power = pow10(-decShift);
tomhudson 2016/07/15 20:45:41 (OK, convinced myself that in the common case pow1
hal.canary 2016/07/17 02:33:42 Done.
321 // Any fewer significant digits loses precision. The unit test 351 int32_t d = (int32_t)(value * power + 0.5);
322 // checks round-trip correctness. 352 //SkASSERT(value == (float)(d * pow(10.0, decShift)));
323 SkASSERT(intPart >= 0.0 && fracPart >= 0.0); // negative handled already. 353 SkASSERT(d <= 999999999);
324 SkASSERT(intPart > 0.0 || fracPart > 0.0); // zero already caught. 354 if (d > 167772159) { // floor(pow(10,1+log10(1<<24)))
325 if (intPart > 0.0) { 355 // need one fewer decimal digits for 24-bit precision.
326 // put the intPart digits onto a stack for later reversal. 356 decShift = decimalExponents - 7;
327 char reversed[1 + FLT_MAX_10_EXP]; // 39 == 1 + FLT_MAX_10_EXP 357 //SkASSERT(power * 0.1 = pow10(-decShift));
328 // the largest integer part is FLT_MAX; it has 39 decimal digits. 358 d = (int32_t)(value * (power * 0.1) + 0.5);
329 size_t reversedIndex = 0; 359 SkASSERT(d <= 99999999);
360 }
361 while (d % 10 == 0) {
362 d /= 10;
363 ++decShift;
364 }
365 SkASSERT(d > 0);
366 //SkASSERT(value == (float)(d * pow(10.0, decShift)));
367 uint8_t buffer[9]; // decimal value buffer.
368 int bufferIndex = 0;
369 do {
370 buffer[bufferIndex++] = d % 10;
371 d /= 10;
372 } while (d != 0);
373 SkASSERT(bufferIndex <= (int)sizeof(buffer) && bufferIndex > 0);
374 if (decShift >= 0) {
330 do { 375 do {
331 SkASSERT(reversedIndex < sizeof(reversed)); 376 --bufferIndex;
332 int digit = static_cast<int>(std::fmod(intPart, 10.0)); 377 *output++ = '0' + buffer[bufferIndex];
333 SkASSERT(digit >= 0 && digit <= 9); 378 } while (bufferIndex);
334 reversed[reversedIndex++] = '0' + digit; 379 for (int i = 0; i < decShift; ++i) {
335 intPart = std::floor(intPart / 10.0); 380 *output++ = '0';
336 } while (intPart > 0.0);
337 significantDigits = reversedIndex;
338 SkASSERT(reversedIndex <= sizeof(reversed));
339 SkASSERT(output + reversedIndex <= end);
340 while (reversedIndex-- > 0) { // pop from stack, append to result
341 *output++ = reversed[reversedIndex];
342 } 381 }
343 } 382 } else {
344 if (fracPart > 0 && significantDigits < maxSignificantDigits) { 383 int N = bufferIndex + decShift;
345 *output++ = '.'; 384 if (N > 0) {
346 SkASSERT(output <= end); 385 for (int i = 0; i < N; ++i) {
347 do { 386 --bufferIndex;
348 fracPart = std::modf(fracPart * 10.0, &intPart); 387 *output++ = '0' + buffer[bufferIndex];
349 int digit = static_cast<int>(intPart);
350 SkASSERT(digit >= 0 && digit <= 9);
351 *output++ = '0' + digit;
352 SkASSERT(output <= end);
353 if (digit > 0 || significantDigits > 0) {
354 // start counting significantDigits after first non-zero digit.
355 ++significantDigits;
356 } 388 }
357 } while (fracPart > 0.0 389 *output++ = '.';
358 && significantDigits < maxSignificantDigits 390 } else {
359 && output < end); 391 *output++ = '.';
360 // When fracPart == 0, additional digits will be zero. 392 for (int i = 0; i < -N; ++i) {
361 // When significantDigits == maxSignificantDigits, we can stop. 393 *output++ = '0';
362 // when output == end, we have filed the string. 394 }
363 // Note: denormalized numbers will not have the same number of 395 }
364 // significantDigits, but do not need them to round-trip. 396 while (bufferIndex > 0) {
397 --bufferIndex;
398 *output++ = '0' + buffer[bufferIndex];
399 if (output == end) {
400 break; // denormalized: don't need extra precision.
401 // Note: denormalized numbers will not have the same number of
402 // significantDigits, but do not need them to round-trip.
403 }
404 }
365 } 405 }
366 SkASSERT(output <= end); 406 SkASSERT(output <= end);
367 *output = '\0'; 407 *output = '\0';
368 return output - result; 408 return output - result;
369 } 409 }
370 410
371 void SkPDFUtils::WriteString(SkWStream* wStream, const char* cin, size_t len) { 411 void SkPDFUtils::WriteString(SkWStream* wStream, const char* cin, size_t len) {
372 SkDEBUGCODE(static const size_t kMaxLen = 65535;) 412 SkDEBUGCODE(static const size_t kMaxLen = 65535;)
373 SkASSERT(len <= kMaxLen); 413 SkASSERT(len <= kMaxLen);
374 414
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 uint8_t c = static_cast<uint8_t>(cin[i]); 446 uint8_t c = static_cast<uint8_t>(cin[i]);
407 static const char gHex[] = "0123456789ABCDEF"; 447 static const char gHex[] = "0123456789ABCDEF";
408 char hexValue[2]; 448 char hexValue[2];
409 hexValue[0] = gHex[(c >> 4) & 0xF]; 449 hexValue[0] = gHex[(c >> 4) & 0xF];
410 hexValue[1] = gHex[ c & 0xF]; 450 hexValue[1] = gHex[ c & 0xF];
411 wStream->write(hexValue, 2); 451 wStream->write(hexValue, 2);
412 } 452 }
413 wStream->writeText(">"); 453 wStream->writeText(">");
414 } 454 }
415 } 455 }
OLDNEW
« no previous file with comments | « no previous file | tests/PDFPrimitivesTest.cpp » ('j') | tests/PDFPrimitivesTest.cpp » ('J')

Powered by Google App Engine
This is Rietveld 408576698