Add exact version of qcms used by Chrome for testing and comparison

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2014023003

Review-Url: https://codereview.chromium.org/2014023003
This commit is contained in:
msarett 2016-05-26 11:05:37 -07:00 committed by Commit bot
parent a62038c478
commit 0e5b249e54
32 changed files with 8286 additions and 0 deletions

View File

@ -12,6 +12,7 @@
'standalone_static_library': 1,
'dependencies': [
'core.gyp:*',
'qcms.gyp:qcms',
],
'include_dirs': [
'../include/effects',

74
gyp/qcms.gyp Normal file
View File

@ -0,0 +1,74 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This build file has been adapted for use in Skia. The contents of third_party/qcms
# are copied directly from Chromium.
{
'variables': {
'skia_warnings_as_errors': 0,
},
'targets': [
{
'target_name': 'qcms',
'type': 'static_library',
# Warning (sign-conversion) fixed upstream by large refactoring. Can be
# removed on next roll.
'msvs_disabled_warnings': [ 4018 ],
'direct_dependent_settings': {
'include_dirs': [
'./src',
],
},
'sources': [
'../third_party/qcms/src/chain.c',
'../third_party/qcms/src/chain.h',
'../third_party/qcms/src/iccread.c',
'../third_party/qcms/src/matrix.c',
'../third_party/qcms/src/matrix.h',
'../third_party/qcms/src/qcms.h',
'../third_party/qcms/src/qcmsint.h',
'../third_party/qcms/src/qcmstypes.h',
'../third_party/qcms/src/qcms_util.c',
'../third_party/qcms/src/transform.c',
'../third_party/qcms/src/transform_util.c',
'../third_party/qcms/src/transform_util.h',
],
'conditions': [
['"x86" in skia_arch_type', {
'defines': [
'SSE2_ENABLE',
],
'sources': [
'../third_party/qcms/src/transform-sse2.c',
],
}],
['skia_os == "win"', {
'msvs_disabled_warnings': [
4056, # overflow in floating-point constant arithmetic (INFINITY)
4756, # overflow in constant arithmetic (INFINITY)
],
}],
],
# Disable warnings
'cflags': [
'-w',
],
'xcode_settings': {
'WARNING_CFLAGS': [
'-w'
],
},
'msvs_settings': {
'VCCLCompilerTool': {
'WarningLevel': '0',
},
},
},
],
}

77
third_party/qcms/BUILD.gn vendored Normal file
View File

@ -0,0 +1,77 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
config("qcms_config") {
include_dirs = [ "src" ]
}
# Do not build QCMS on Android or iOS. (See http://crbug.com/577155)
disable_qcms = is_android || is_ios
source_set("qcms") {
if (disable_qcms) {
sources = [
"src/empty.c",
]
} else {
sources = [
"src/chain.c",
"src/chain.h",
"src/iccread.c",
"src/matrix.c",
"src/matrix.h",
"src/qcms.h",
"src/qcms_util.c",
"src/qcmsint.h",
"src/qcmstypes.h",
"src/transform.c",
"src/transform_util.c",
"src/transform_util.h",
]
if (current_cpu == "x86" || current_cpu == "x64") {
defines = [ "SSE2_ENABLE" ]
sources += [ "src/transform-sse2.c" ]
}
}
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [ "//build/config/compiler:no_chromium_code" ]
public_configs = [ ":qcms_config" ]
if (is_win) {
cflags = [
"/wd4018", # Signed/unsigned mismatch in comparison.
"/wd4056", # Overflow in floating-point constant arithmetic (INFINITY).
"/wd4756", # Overflow in constant arithmetic (INFINITY).
]
}
}
if (!disable_qcms) {
executable("qcms_tests") {
if (current_cpu == "x86" || current_cpu == "x64") {
defines = [ "SSE2_ENABLE" ]
}
sources = [
"src/tests/qcms_test_internal_srgb.c",
"src/tests/qcms_test_main.c",
"src/tests/qcms_test_munsell.c",
"src/tests/qcms_test_ntsc_gamut.c",
"src/tests/qcms_test_output_trc.c",
"src/tests/qcms_test_tetra_clut_rgba.c",
"src/tests/qcms_test_util.c",
]
deps = [
"//build/config/sanitizers:deps",
"//build/win:default_exe_manifest",
"//third_party/qcms",
]
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [ "//build/config/compiler:no_chromium_code" ]
public_configs = [ ":qcms_config" ]
}
}

162
third_party/qcms/README.chromium vendored Normal file
View File

@ -0,0 +1,162 @@
Name: Quick Color Management System
Short Name: qcms
URL: https://github.com/jrmuizel/qcms/tree/v4
Version: v4
Date: 2012-03-13
License: MIT
License File: src/COPYING
Security Critical: yes
Description:
Contains support for applying an ICC color profile to an image. The code is
originally based on tinycms, re-written by Mozilla for better security and
performance. This copy is a source-drop from Mozilla on March 13, 2012.
Local Modifications:
Some files only have license headers in the master branch.
- Added the same license headers to the versions brought down from the 'v4'
branch src URL qcms/tree/v4
The following changes have been made since qcms was imported:
- Add bgra output support.
- Use HAVE_POSIX_MEMALIGN instead of HAS_POSIX_MEMALIG
(https://bugzilla.mozilla.org/show_bug.cgi?id=692922)
- Applied upstream patch:
- https://bug752254.bugzilla.mozilla.org/attachment.cgi?id=626102
- Applied upstream patch for sanitizing gamma table:
- Expanded gamma clamp range to allow 1.0.
- Do not short-circuit bogus profile check for A2B0 or B2A0 unless v4 enabled.
- Only reference code from transform-sse1.c and transform-sse2.c when SSE is
enabled.
- Limit the use an x86-only attribute to 32-bit x86 builds.
- https://code.google.com/p/chromium/issues/detail?id=490016
- Fix integer truncation warning/errors on Win64 build.
- Apply upstream thread safety (fix) patch from
- https://bugzilla.mozilla.org/show_bug.cgi?id=853169
- Apply upstream fix for qcms_profile_from_memory from
- https://bugzilla.mozilla.org/show_bug.cgi?id=969226
- Apply upstream simplification of qcmstypes.h
- https://hg.mozilla.org/mozilla-central/rev/b5be94737a83
- Check for unused tag_len in read_nested_curveType()
- Apply qcms_transform_precacheLUT_float does not properly free dest buffer
- https://bugzilla.mozilla.org/show_bug.cgi?id=701348
- Add qcms_profile_match api
- https://code.google.com/p/chromium/issues/detail?id=401971
- Add qcms_profile_get_description api
- https://code.google.com/p/chromium/issues/detail?id=401971
- Convert MLUC profile description embedded nulls to '.'
- https://code.google.com/p/chromium/issues/detail?id=401971
- [APPLE port] Use en-localized profile description names
- https://code.google.com/p/chromium/issues/detail?id=401971
- [APPLE port] Use mmod data for profile descriptions
- https://code.google.com/p/chromium/issues/detail?id=401971
- Minor variable name change: description -> description_offset
- https://code.google.com/p/chromium/issues/detail?id=401971
- Avoid divisions creating sample points in the float cube LUT builder
- https://code.google.com/p/chromium/issues/detail?id=443863
- Add bgra (z,y,x) sampled transform lookup table api
- https://code.google.com/p/chromium/issues/detail?id=443863
- Apply upstream fix reject invalid sizes from
- https://bugzilla.mozilla.org/show_bug.cgi?id=1132468
- lut_inverse_interp16: remove odd whitespace formatting
- https://code.google.com/p/chromium/issues/detail?id=458024
- lut_inverse_interp16: better handle degenerate TRC curves
- https://code.google.com/p/chromium/issues/detail?id=458024
- Add vcgt tag reader and API to read its RGB gamma data
- https://code.google.com/p/chromium/issues/detail?id=471749
- Consolidate loop over channels and elements in VCGT parser
- https://code.google.com/p/chromium/issues/detail?id=471749
- Limit vcgt table to a maximum of 1024 entries
- https://code.google.com/p/chromium/issues/detail?id=471749
- Add float to half float conversion routine
- https://code.google.com/p/chromium/issues/detail?id=491784
- Use half float conversion tables (not functions)
- https://code.google.com/p/chromium/issues/detail?id=491784
- Add matrix transform flag and matrix extraction api
- https://code.google.com/p/chromium/issues/detail?id=491826
- Add tone reproduction curve (TRC) extraction api
- https://code.google.com/p/chromium/issues/detail?id=491826
- lut_inverse_interp16: interpolate degenerate zeros in TRC curves
- https://code.google.com/p/chromium/issues/detail?id=458024
- Make half float code self-contained: add halffloat.h
- https://code.google.com/p/chromium/issues/detail?id=491784
- lut_inverse_interp16: fix black level inversion error
- https://code.google.com/p/chromium/issues/detail?id=504681
- Apply upstream fix of uninitialized value
- https://github.com/jrmuizel/qcms/commit/0edb697
- Apply Keep the output of the TRC between 0 and 1
- https://bugzilla.mozilla.org/show_bug.cgi?id=764181
- Drop SSE1 code as Chromium mandates SSE2 as a minimum requirement
- https://code.google.com/p/chromium/issues/detail?id=506856
- Apply Fix potential leaks in qcms_transform_create
- https://bugzilla.mozilla.org/show_bug.cgi?id=1168480
- Apply fix for crash in qcms_transform_data_rgb_out_lut_sse1
- https://bugzilla.mozilla.org/show_bug.cgi?id=1163740
- Add SSE2 code for qcms_transform_data_rgba_out_lut_sse2
- https://code.google.com/p/chromium/issues/detail?id=506607
- Turbo charge SSE2 qcms_transform_data_rgba_out_lut_sse2
- https://code.google.com/p/chromium/issues/detail?id=506607
- Check matrix transforms have the same PCS
- https://code.google.com/p/chromium/issues/detail?id=510682
- Apply Don't reject valid profiles with no CLUT offset
- https://bugzilla.mozilla.org/show_bug.cgi?id=1176551
- Runtime detect qcms_transform_data_tetra_clut_rgba_sse2 use
- https://code.google.com/p/chromium/issues/detail?id=506607
- LUT8/16 profiles with an empty CLUT are invalid
- https://code.google.com/p/chromium/issues/detail?id=487284
- Caching transform prelude to save computation in V4 profiles
- https://code.google.com/p/chromium/issues/detail?id=506607
- Use the caching transform prelude for non-SSE code in transform.c
- https://code.google.com/p/chromium/issues/detail?id=506607
- Expand QCMS tests. Add Munsell test for transform accuracy
- https://code.google.com/p/chromium/issues/detail?id=532910
- Compute RMS error from color error dE in the Munsell test
- https://code.google.com/p/chromium/issues/detail?id=532910
- Store color profile version and add a version read API
- https://code.google.com/p/chromium/issues/detail?id=532258
- Add support for VCGT tag formula gamma
- https://code.google.com/p/chromium/issues/detail?id=551568
- Minimize transform RMS in qcms_transform_data_rgb(a)_out_lut
- https://code.google.com/p/chromium/issues/detail?id=532910
- Allow negative XYZ for display profiles on the APPLE port
- https://code.google.com/p/chromium/issues/detail?id=562951
- Add a color profile white point transform api
- https://code.google.com/p/chromium/issues/detail?id=564355
- Compute the pole and zero values needed by lut_inverse_interp16 once
- https://code.google.com/p/chromium/issues/detail?id=505793
- Store the profile media white point during iccread.c
- https://code.google.com/p/chromium/issues/detail?id=565222
- Add an API to check for profile media white point
- https://code.google.com/p/chromium/issues/detail?id=565222
- Add a qcms_profile_get_white_point() api
- https://code.google.com/p/chromium/issues/detail?id=565222
- Set the media white point of the sRGB profile
- https://code.google.com/p/chromium/issues/detail?id=565222
- Don't build QCMS on Android and iOS
- https://code.google.com/p/chromium/issues/detail?id=577155
- Fix para curve tag interval paramater use
- https://code.google.com/p/chromium/issues/detail?id=580920
- Verify internal sRGB profile tristimulus match the standard sRGB profile
- https://code.google.com/p/chromium/issues/detail?id=580917
- Expose unsigned short version of TRC curve
- https://code.google.com/p/chromium/issues/detail?id=495196
- Add qcms_profile_ntsc_relative_gamut_size api
- https://code.google.com/p/chromium/issues/detail?id=582870
- Fix build issue of qcms_tests for MIPS Linux
- https://code.google.com/p/chromium/issues/detail?id=590227
- Update primaries used to build internal sRGB profile
- https://code.google.com/p/chromium/issues/detail?id=580917
- Update internal sRGB profile test report output
- https://code.google.com/p/chromium/issues/detail?id=580917
- Add more internal sRGB profile tests
- https://code.google.com/p/chromium/issues/detail?id=580917
- Fix build_output_lut to return correct data for parametric curves
- https://bugs.chromium.org/p/chromium/issues/detail?id=600338
- Make build_output_lut output 4096 points for parametric curves
- https://bugs.chromium.org/p/chromium/issues/detail?id=600338
- Use a static table in build_output_lut to invert para curves
- https://bugs.chromium.org/p/chromium/issues/detail?id=600338
- Make qcms_test_output_trc handle normal gamma curves
- https://bugs.chromium.org/p/chromium/issues/detail?id=600338
For the Chromium changes, since the import, in a patch format run:
git diff b8456f38 src

113
third_party/qcms/qcms.gyp vendored Normal file
View File

@ -0,0 +1,113 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'variables': {
'conditions': [
# Do not build QCMS on Android or iOS. (See http://crbug.com/577155)
['OS == "android" or OS == "ios"', {
'disable_qcms%': 1,
}, {
'disable_qcms%': 0,
}],
],
},
'targets': [
{
'target_name': 'qcms',
'product_name': 'qcms',
'type': 'static_library',
# Warning (sign-conversion) fixed upstream by large refactoring. Can be
# removed on next roll.
'msvs_disabled_warnings': [ 4018 ],
'direct_dependent_settings': {
'include_dirs': [
'./src',
],
},
'conditions': [
['disable_qcms == 1', {
'sources': [
'src/empty.c',
],
}, { # disable_qcms == 0
'sources': [
'src/chain.c',
'src/chain.h',
'src/iccread.c',
'src/matrix.c',
'src/matrix.h',
'src/qcms.h',
'src/qcmsint.h',
'src/qcmstypes.h',
'src/qcms_util.c',
'src/transform.c',
'src/transform_util.c',
'src/transform_util.h',
],
'conditions': [
['target_arch=="ia32" or target_arch=="x64"', {
'defines': [
'SSE2_ENABLE',
],
'sources': [
'src/transform-sse2.c',
],
}],
],
}],
['OS == "win"', {
'msvs_disabled_warnings': [
4056, # overflow in floating-point constant arithmetic (INFINITY)
4756, # overflow in constant arithmetic (INFINITY)
],
}],
],
},
],
'conditions': [
['disable_qcms == 0', {
'targets': [
{
'target_name': 'qcms_tests',
'product_name': 'qcms_tests',
'type': 'executable',
'dependencies': [
'qcms',
],
'conditions': [
['OS != "win"', {
'libraries': [
'-lm',
],
}],
['target_arch=="ia32" or target_arch=="x64"', {
'defines': [
'SSE2_ENABLE',
],
}],
],
'sources': [
'src/tests/qcms_test_main.c',
'src/tests/qcms_test_internal_srgb.c',
'src/tests/qcms_test_munsell.c',
'src/tests/qcms_test_ntsc_gamut.c',
'src/tests/qcms_test_output_trc.c',
'src/tests/qcms_test_tetra_clut_rgba.c',
'src/tests/qcms_test_util.c',
],
},
],
}],
],
}
# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:

21
third_party/qcms/src/COPYING vendored Normal file
View File

@ -0,0 +1,21 @@
qcms
Copyright (C) 2009 Mozilla Corporation
Copyright (C) 1998-2007 Marti Maria
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

9
third_party/qcms/src/README vendored Normal file
View File

@ -0,0 +1,9 @@
qcms
====
Quick color management.
Current limitations
-----------
- Only supports RGB and Gray colorspaces. e.g. no support for CMYK yet.
- Only packed RGB and RGBA, K, and KA.

1026
third_party/qcms/src/chain.c vendored Normal file

File diff suppressed because it is too large Load Diff

30
third_party/qcms/src/chain.h vendored Normal file
View File

@ -0,0 +1,30 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef _QCMS_CHAIN_H
#define _QCMS_CHAIN_H
// Generates and returns a 3D LUT with lutSize^3 samples using the provided src/dest.
float* qcms_chain_transform(qcms_profile *in, qcms_profile *out, float *src, float *dest, size_t lutSize);
#endif

6
third_party/qcms/src/empty.c vendored Normal file
View File

@ -0,0 +1,6 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
/*
* qcms is not built on this port: see http://crbug.com/577155
*/

106
third_party/qcms/src/halffloat.h vendored Normal file
View File

@ -0,0 +1,106 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef _QCMS_HALFFLOAT_H
#define _QCMS_HALFFLOAT_H
const unsigned short qcms_half_float_base_table[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256,
512, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216, 10240, 11264, 12288, 13312, 14336, 15360,
16384, 17408, 18432, 19456, 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720, 31744,
31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
32768, 32768, 32768, 32768, 32768, 32768, 32768, 32769, 32770, 32772, 32776, 32784, 32800, 32832, 32896, 33024,
33280, 33792, 34816, 35840, 36864, 37888, 38912, 39936, 40960, 41984, 43008, 44032, 45056, 46080, 47104, 48128,
49152, 50176, 51200, 52224, 53248, 54272, 55296, 56320, 57344, 58368, 59392, 60416, 61440, 62464, 63488, 64512,
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512
};
const unsigned char qcms_half_float_shift_table[512] = {
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15,
14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15,
14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13
};
static inline unsigned short float_to_half_float(float f)
{
// See Blink::Source/platform/graphics/gpu/WebGLImageConversion.cpp::convertFloatToHalfFloat() and http://crbug.com/491784
unsigned temp = *((unsigned *)(&f));
unsigned signexp = (temp >> 23) & 0x1ff;
return qcms_half_float_base_table[signexp] + ((temp & 0x007fffff) >> qcms_half_float_shift_table[signexp]);
}
#endif

1645
third_party/qcms/src/iccread.c vendored Normal file

File diff suppressed because it is too large Load Diff

136
third_party/qcms/src/matrix.c vendored Normal file
View File

@ -0,0 +1,136 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <stdlib.h>
#include "qcmsint.h"
#include "matrix.h"
struct vector matrix_eval(struct matrix mat, struct vector v)
{
struct vector result;
result.v[0] = mat.m[0][0]*v.v[0] + mat.m[0][1]*v.v[1] + mat.m[0][2]*v.v[2];
result.v[1] = mat.m[1][0]*v.v[0] + mat.m[1][1]*v.v[1] + mat.m[1][2]*v.v[2];
result.v[2] = mat.m[2][0]*v.v[0] + mat.m[2][1]*v.v[1] + mat.m[2][2]*v.v[2];
return result;
}
//XXX: should probably pass by reference and we could
//probably reuse this computation in matrix_invert
float matrix_det(struct matrix mat)
{
float det;
det = mat.m[0][0]*mat.m[1][1]*mat.m[2][2] +
mat.m[0][1]*mat.m[1][2]*mat.m[2][0] +
mat.m[0][2]*mat.m[1][0]*mat.m[2][1] -
mat.m[0][0]*mat.m[1][2]*mat.m[2][1] -
mat.m[0][1]*mat.m[1][0]*mat.m[2][2] -
mat.m[0][2]*mat.m[1][1]*mat.m[2][0];
return det;
}
/* from pixman and cairo and Mathematics for Game Programmers */
/* lcms uses gauss-jordan elimination with partial pivoting which is
* less efficient and not as numerically stable. See Mathematics for
* Game Programmers. */
struct matrix matrix_invert(struct matrix mat)
{
struct matrix dest_mat;
int i,j;
static int a[3] = { 2, 2, 1 };
static int b[3] = { 1, 0, 0 };
/* inv (A) = 1/det (A) * adj (A) */
float det = matrix_det(mat);
if (det == 0) {
dest_mat.invalid = true;
} else {
dest_mat.invalid = false;
}
det = 1/det;
for (j = 0; j < 3; j++) {
for (i = 0; i < 3; i++) {
double p;
int ai = a[i];
int aj = a[j];
int bi = b[i];
int bj = b[j];
p = mat.m[ai][aj] * mat.m[bi][bj] -
mat.m[ai][bj] * mat.m[bi][aj];
if (((i + j) & 1) != 0)
p = -p;
dest_mat.m[j][i] = det * p;
}
}
return dest_mat;
}
struct matrix matrix_identity(void)
{
struct matrix i;
i.m[0][0] = 1;
i.m[0][1] = 0;
i.m[0][2] = 0;
i.m[1][0] = 0;
i.m[1][1] = 1;
i.m[1][2] = 0;
i.m[2][0] = 0;
i.m[2][1] = 0;
i.m[2][2] = 1;
i.invalid = false;
return i;
}
struct matrix matrix_invalid(void)
{
struct matrix inv = matrix_identity();
inv.invalid = true;
return inv;
}
/* from pixman */
/* MAT3per... */
struct matrix matrix_multiply(struct matrix a, struct matrix b)
{
struct matrix result;
int dx, dy;
int o;
for (dy = 0; dy < 3; dy++) {
for (dx = 0; dx < 3; dx++) {
double v = 0;
for (o = 0; o < 3; o++) {
v += a.m[dy][o] * b.m[o][dx];
}
result.m[dy][dx] = v;
}
}
result.invalid = a.invalid || b.invalid;
return result;
}

39
third_party/qcms/src/matrix.h vendored Normal file
View File

@ -0,0 +1,39 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef _QCMS_MATRIX_H
#define _QCMS_MATRIX_H
struct vector {
float v[3];
};
struct vector matrix_eval(struct matrix mat, struct vector v);
float matrix_det(struct matrix mat);
struct matrix matrix_identity(void);
struct matrix matrix_multiply(struct matrix a, struct matrix b);
struct matrix matrix_invert(struct matrix mat);
struct matrix matrix_invalid(void);
#endif

171
third_party/qcms/src/qcms.h vendored Normal file
View File

@ -0,0 +1,171 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef QCMS_H
#define QCMS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
struct _qcms_profile;
typedef struct _qcms_profile qcms_profile;
struct _qcms_transform;
typedef struct _qcms_transform qcms_transform;
typedef int qcms_bool;
/* ICC Section 6.1.5 Color Space Signatures (abridged) */
typedef enum {
XYZData /* XYZ */ = 0x58595A20,
labData /* Lab */ = 0x4C616220,
luvData /* Luv */ = 0x4C757620,
YCbCrData /* YCbr' */ = 0x59436272,
YxyData /* Yxy */ = 0x59787920,
rgbData /* RGB */ = 0x52474220,
grayData /* GRAY */ = 0x47524159,
hsvData /* HSV */ = 0x48535620,
hlsData /* HLS */ = 0x484C5320,
cmykData /* CMYK */ = 0x434D594B,
cmyData /* CMY */ = 0x434D5920,
} qcms_color_space;
/* ICC Section 6.1.11 Rendering Intents */
typedef enum {
QCMS_INTENT_DEFAULT = 0,
QCMS_INTENT_PERCEPTUAL = 0,
QCMS_INTENT_RELATIVE_COLORIMETRIC = 1,
QCMS_INTENT_SATURATION = 2,
QCMS_INTENT_ABSOLUTE_COLORIMETRIC = 3
} qcms_intent;
/* Input data formats */
typedef enum {
QCMS_DATA_RGB_8,
QCMS_DATA_RGBA_8,
QCMS_DATA_GRAY_8,
QCMS_DATA_GRAYA_8
} qcms_data_type;
/* Output data format for qcms_transform_data_type() */
typedef enum {
QCMS_OUTPUT_RGBX,
QCMS_OUTPUT_BGRX
} qcms_output_type;
/* Output data format for qcms_transform_get_input|output_trc_rgba() */
typedef enum {
QCMS_TRC_PARAMETRIC, // Not implemented.
QCMS_TRC_FLOAT, // Not implemented.
QCMS_TRC_HALF_FLOAT, // IEE754: binary16.
QCMS_TRC_USHORT, // 0.16 fixed point.
} qcms_trc_type;
typedef struct {
double x;
double y;
double Y;
} qcms_CIE_xyY;
typedef struct {
qcms_CIE_xyY red;
qcms_CIE_xyY green;
qcms_CIE_xyY blue;
} qcms_CIE_xyYTRIPLE;
typedef struct {
float X;
float Y;
float Z;
} qcms_xyz_float;
qcms_profile* qcms_profile_create_rgb_with_gamma(
qcms_CIE_xyY white_point,
qcms_CIE_xyYTRIPLE primaries,
float gamma);
qcms_profile* qcms_profile_from_memory(const void *mem, size_t size);
qcms_profile* qcms_profile_from_file(FILE *file);
qcms_profile* qcms_profile_from_path(const char *path);
#ifdef _WIN32
qcms_profile* qcms_profile_from_unicode_path(const wchar_t *path);
#endif
qcms_profile* qcms_profile_sRGB(void);
void qcms_profile_release(qcms_profile *profile);
qcms_bool qcms_profile_is_bogus(qcms_profile *profile);
qcms_bool qcms_profile_has_white_point(qcms_profile *profile);
qcms_xyz_float qcms_profile_get_white_point(qcms_profile *profile);
qcms_intent qcms_profile_get_rendering_intent(qcms_profile *profile);
qcms_color_space qcms_profile_get_color_space(qcms_profile *profile);
unsigned qcms_profile_get_version(qcms_profile *profile);
qcms_bool qcms_profile_white_transform(qcms_profile *profile, float XYZ[3]);
qcms_bool qcms_profile_match(qcms_profile *p1, qcms_profile *p2);
const char* qcms_profile_get_description(qcms_profile *profile);
void qcms_profile_precache_output_transform(qcms_profile *profile);
size_t qcms_profile_get_vcgt_channel_length(qcms_profile *profile);
qcms_bool qcms_profile_get_vcgt_rgb_channels(qcms_profile *profile, unsigned short *data);
float qcms_profile_ntsc_relative_gamut_size(qcms_profile *profile);
qcms_transform* qcms_transform_create(
qcms_profile *in, qcms_data_type in_type,
qcms_profile *out, qcms_data_type out_type,
qcms_intent intent);
size_t qcms_transform_get_input_trc_rgba(
qcms_transform *transform, qcms_profile *in, qcms_trc_type type, unsigned short *data);
size_t qcms_transform_get_output_trc_rgba(
qcms_transform *transform, qcms_profile *out, qcms_trc_type type, unsigned short *data);
qcms_bool qcms_transform_is_matrix(qcms_transform *transform);
float qcms_transform_get_matrix(qcms_transform *transform, unsigned i, unsigned j);
qcms_bool qcms_transform_create_LUT_zyx_bgra(
qcms_profile *in, qcms_profile *out, qcms_intent intent,
int samples, unsigned char* lut);
void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_t length);
void qcms_transform_data_type(qcms_transform *transform, void *src, void *dest, size_t length, qcms_output_type type);
void qcms_transform_release(qcms_transform *);
void qcms_enable_iccv4();
#ifdef __cplusplus
}
#endif
/*
* In general, QCMS is not threadsafe. However, it should be safe to create
* profile and transformation objects on different threads, so long as you
* don't use the same objects on different threads at the same time.
*/
#endif

69
third_party/qcms/src/qcms_util.c vendored Normal file
View File

@ -0,0 +1,69 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcmsint.h"
#include <math.h>
typedef struct _qcms_coords {
float x;
float y;
} qcms_coords;
typedef struct _qcms_triangle {
qcms_coords verticies[3];
} qcms_triangle;
#define NTSC_1953_GAMUT_SIZE 0.1582
static qcms_triangle get_profile_triangle(qcms_profile *profile)
{
float sumRed = s15Fixed16Number_to_float(profile->redColorant.X) +
s15Fixed16Number_to_float(profile->redColorant.Y) +
s15Fixed16Number_to_float(profile->redColorant.Z);
float xRed = s15Fixed16Number_to_float(profile->redColorant.X) / sumRed;
float yRed = s15Fixed16Number_to_float(profile->redColorant.Y) / sumRed;
float sumGreen = s15Fixed16Number_to_float(profile->greenColorant.X) +
s15Fixed16Number_to_float(profile->greenColorant.Y) +
s15Fixed16Number_to_float(profile->greenColorant.Z);
float xGreen = s15Fixed16Number_to_float(profile->greenColorant.X) / sumGreen;
float yGreen = s15Fixed16Number_to_float(profile->greenColorant.Y) / sumGreen;
float sumBlue = s15Fixed16Number_to_float(profile->blueColorant.X) +
s15Fixed16Number_to_float(profile->blueColorant.Y) +
s15Fixed16Number_to_float(profile->blueColorant.Z);
float xBlue = s15Fixed16Number_to_float(profile->blueColorant.X) / sumBlue;
float yBlue = s15Fixed16Number_to_float(profile->blueColorant.Y) / sumBlue;
qcms_triangle triangle = {{{xRed, yRed}, {xGreen, yGreen}, {xBlue, yBlue}}};
return triangle;
}
static float get_triangle_area(const qcms_triangle candidate)
{
float xRed = candidate.verticies[0].x;
float yRed = candidate.verticies[0].y;
float xGreen = candidate.verticies[1].x;
float yGreen = candidate.verticies[1].y;
float xBlue = candidate.verticies[2].x;
float yBlue = candidate.verticies[2].y;
float area = fabs((xRed - xBlue) * (yGreen - yBlue) - (xGreen - xBlue) * (yRed - yBlue)) / 2;
return area;
}
static float get_ntsc_gamut_metric_area(const qcms_triangle candidate)
{
float area = get_triangle_area(candidate);
return area * 100 / NTSC_1953_GAMUT_SIZE;
}
float qcms_profile_ntsc_relative_gamut_size(qcms_profile *profile)
{
qcms_triangle triangle = get_profile_triangle(profile);
return get_ntsc_gamut_metric_area(triangle);
}

342
third_party/qcms/src/qcmsint.h vendored Normal file
View File

@ -0,0 +1,342 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "qcms.h"
#include "qcmstypes.h"
/* used as a lookup table for the output transformation.
* we refcount them so we only need to have one around per output
* profile, instead of duplicating them per transform */
struct precache_output
{
int ref_count;
/* We previously used a count of 65536 here but that seems like more
* precision than we actually need. By reducing the size we can
* improve startup performance and reduce memory usage. ColorSync on
* 10.5 uses 4097 which is perhaps because they use a fixed point
* representation where 1. is represented by 0x1000. */
#define PRECACHE_OUTPUT_SIZE 8192
#define PRECACHE_OUTPUT_MAX (PRECACHE_OUTPUT_SIZE-1)
uint8_t data[PRECACHE_OUTPUT_SIZE];
};
#ifdef _MSC_VER
#define ALIGN __declspec(align(16))
#else
#define ALIGN __attribute__(( aligned (16) ))
#endif
typedef struct _qcms_format_type {
int r;
int b;
} qcms_format_type;
struct _qcms_transform {
float ALIGN matrix[3][4];
float *input_gamma_table_r;
float *input_gamma_table_g;
float *input_gamma_table_b;
float *input_clut_table_r;
float *input_clut_table_g;
float *input_clut_table_b;
uint16_t input_clut_table_length;
float *r_clut;
float *g_clut;
float *b_clut;
uint16_t grid_size;
float *output_clut_table_r;
float *output_clut_table_g;
float *output_clut_table_b;
uint16_t output_clut_table_length;
float *input_gamma_table_gray;
float out_gamma_r;
float out_gamma_g;
float out_gamma_b;
float out_gamma_gray;
uint16_t *output_gamma_lut_r;
uint16_t *output_gamma_lut_g;
uint16_t *output_gamma_lut_b;
uint16_t *output_gamma_lut_gray;
size_t output_gamma_lut_r_length;
size_t output_gamma_lut_g_length;
size_t output_gamma_lut_b_length;
size_t output_gamma_lut_gray_length;
struct precache_output *output_table_r;
struct precache_output *output_table_g;
struct precache_output *output_table_b;
void (*transform_fn)(struct _qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, struct _qcms_format_type output_format);
unsigned char floor_cache[256];
unsigned char ceil_cache[256];
float r_cache[256];
#define TRANSFORM_FLAG_MATRIX 0x0001
#define TRANSFORM_FLAG_CLUT_CACHE 0x0002
uint16_t transform_flags;
};
struct matrix {
float m[3][3];
bool invalid;
};
struct qcms_modular_transform;
typedef void (*transform_module_fn_t)(struct qcms_modular_transform *transform, float *src, float *dest, size_t length);
struct qcms_modular_transform {
struct matrix matrix;
float tx, ty, tz;
float *input_clut_table_r;
float *input_clut_table_g;
float *input_clut_table_b;
uint16_t input_clut_table_length;
float *r_clut;
float *g_clut;
float *b_clut;
uint16_t grid_size;
float *output_clut_table_r;
float *output_clut_table_g;
float *output_clut_table_b;
uint16_t output_clut_table_length;
uint16_t *output_gamma_lut_r;
uint16_t *output_gamma_lut_g;
uint16_t *output_gamma_lut_b;
size_t output_gamma_lut_r_length;
size_t output_gamma_lut_g_length;
size_t output_gamma_lut_b_length;
transform_module_fn_t transform_module_fn;
struct qcms_modular_transform *next_transform;
};
typedef int32_t s15Fixed16Number;
typedef uint16_t uInt16Number;
typedef uint8_t uInt8Number;
struct XYZNumber {
s15Fixed16Number X;
s15Fixed16Number Y;
s15Fixed16Number Z;
};
struct curveType {
uint32_t type;
uint32_t count;
float parameter[7];
uInt16Number data[];
};
struct lutmABType {
uint8_t num_in_channels;
uint8_t num_out_channels;
// 16 is the upperbound, actual is 0..num_in_channels.
uint8_t num_grid_points[16];
s15Fixed16Number e00;
s15Fixed16Number e01;
s15Fixed16Number e02;
s15Fixed16Number e03;
s15Fixed16Number e10;
s15Fixed16Number e11;
s15Fixed16Number e12;
s15Fixed16Number e13;
s15Fixed16Number e20;
s15Fixed16Number e21;
s15Fixed16Number e22;
s15Fixed16Number e23;
// reversed elements (for mBA)
bool reversed;
float *clut_table;
struct curveType *a_curves[10];
struct curveType *b_curves[10];
struct curveType *m_curves[10];
float clut_table_data[];
};
/* should lut8Type and lut16Type be different types? */
struct lutType { // used by lut8Type/lut16Type (mft2) only
uint8_t num_input_channels;
uint8_t num_output_channels;
uint8_t num_clut_grid_points;
s15Fixed16Number e00;
s15Fixed16Number e01;
s15Fixed16Number e02;
s15Fixed16Number e10;
s15Fixed16Number e11;
s15Fixed16Number e12;
s15Fixed16Number e20;
s15Fixed16Number e21;
s15Fixed16Number e22;
uint16_t num_input_table_entries;
uint16_t num_output_table_entries;
float *input_table;
float *clut_table;
float *output_table;
float table_data[];
};
struct vcgtType {
/* data contains three gamma channels: R[length], then G[length], then
* B[length]. */
uint16_t *data;
size_t length;
};
#if 0
/* this is from an intial idea of having the struct correspond to the data in
* the file. I decided that it wasn't a good idea.
*/
struct tag_value {
uint32_t type;
union {
struct {
uint32_t reserved;
struct {
s15Fixed16Number X;
s15Fixed16Number Y;
s15Fixed16Number Z;
} XYZNumber;
} XYZType;
};
}; // I guess we need to pack this?
#endif
#define RGB_SIGNATURE 0x52474220
#define GRAY_SIGNATURE 0x47524159
#define XYZ_SIGNATURE 0x58595A20
#define LAB_SIGNATURE 0x4C616220
struct _qcms_profile {
uint32_t icc_version;
char description[64];
uint32_t class;
uint32_t color_space;
uint32_t pcs;
qcms_intent rendering_intent;
struct XYZNumber mediaWhitePoint;
struct XYZNumber redColorant;
struct XYZNumber blueColorant;
struct XYZNumber greenColorant;
struct curveType *redTRC;
struct curveType *blueTRC;
struct curveType *greenTRC;
struct curveType *grayTRC;
struct lutType *A2B0;
struct lutType *B2A0;
struct lutmABType *mAB;
struct lutmABType *mBA;
struct matrix chromaticAdaption;
struct vcgtType vcgt;
struct precache_output *output_table_r;
struct precache_output *output_table_g;
struct precache_output *output_table_b;
};
#ifdef _MSC_VER
#define inline _inline
#endif
/* produces the nearest float to 'a' with a maximum error
* of 1/1024 which happens for large values like 0x40000040 */
static inline float s15Fixed16Number_to_float(s15Fixed16Number a)
{
return ((int32_t)a)/65536.f;
}
static inline s15Fixed16Number double_to_s15Fixed16Number(double v)
{
return (int32_t)(v*65536);
}
static inline float uInt8Number_to_float(uInt8Number a)
{
return ((int32_t)a)/255.f;
}
static inline float uInt16Number_to_float(uInt16Number a)
{
return ((int32_t)a)/65535.f;
}
void precache_release(struct precache_output *p);
qcms_bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries);
void qcms_transform_data_rgb_out_lut_sse2(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format);
void qcms_transform_data_rgba_out_lut_sse2(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format);
void qcms_transform_data_tetra_clut_rgba_sse2(qcms_transform* transform,
unsigned char* src,
unsigned char* dest,
size_t length,
qcms_format_type output_format);
void qcms_transform_build_clut_cache(qcms_transform* transform);
extern qcms_bool qcms_supports_iccv4;
#ifdef _MSC_VER
long __cdecl _InterlockedIncrement(long volatile *);
long __cdecl _InterlockedDecrement(long volatile *);
#pragma intrinsic(_InterlockedIncrement)
#pragma intrinsic(_InterlockedDecrement)
#define qcms_atomic_increment(x) _InterlockedIncrement((long volatile *)&x)
#define qcms_atomic_decrement(x) _InterlockedDecrement((long volatile*)&x)
#else
#define qcms_atomic_increment(x) __sync_add_and_fetch(&x, 1)
#define qcms_atomic_decrement(x) __sync_sub_and_fetch(&x, 1)
#endif

77
third_party/qcms/src/qcmstypes.h vendored Normal file
View File

@ -0,0 +1,77 @@
// qcms
// Copyright (C) 2009 Mozilla Foundation
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef QCMS_TYPES_H
#define QCMS_TYPES_H
#if BYTE_ORDER == LITTLE_ENDIAN
#define IS_LITTLE_ENDIAN
#elif BYTE_ORDER == BIG_ENDIAN
#define IS_BIG_ENDIAN
#endif
/* all of the platforms that we use _MSC_VER on are little endian
* so this is sufficient for now */
#ifdef _MSC_VER
#define IS_LITTLE_ENDIAN
#endif
#ifdef __OS2__
#define IS_LITTLE_ENDIAN
#endif
#if !defined(IS_LITTLE_ENDIAN) && !defined(IS_BIG_ENDIAN)
#error Unknown endianess
#endif
#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || defined (_sgi) || defined (__sun) || defined (sun) || defined (__digital__)
# include <inttypes.h>
#elif defined (_MSC_VER) && _MSC_VER < 1600
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#ifdef _WIN64
typedef unsigned __int64 uintptr_t;
#else
#pragma warning(push)
/* Disable benign redefinition of type warning 4142 */
#pragma warning(disable:4142)
typedef unsigned long uintptr_t;
/* Restore warnings */
#pragma warning(pop)
#endif
#elif defined (_AIX)
# include <sys/inttypes.h>
#else
# include <stdint.h>
#endif
typedef qcms_bool bool;
#define true 1
#define false 0
#endif

16
third_party/qcms/src/tests/Makefile vendored Normal file
View File

@ -0,0 +1,16 @@
CC=gcc
INCLUDE=-I../
WALL=-Wall
CFLAGS=-O2 -msse2 $(WALL) -DSSE2_ENABLE
LDFLAGS=-lm
QCMS=../transform.c ../transform-sse2.c ../transform_util.c ../matrix.c ../iccread.c ../chain.c ../qcms_util.c
OBJS=$(QCMS:.c=.o)
all: qcms_tests
qcms_tests: qcms_test_*.c $(OBJS)
$(CC) $(CFLAGS) $(INCLUDE) $^ -o $@ $(LDFLAGS)
clean:
rm -rf qcms_tests *.o $(OBJS)

View File

@ -0,0 +1,155 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcms.h"
#include "qcms_test_util.h"
#include <assert.h>
#include <math.h> // sqrt
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#ifndef DISPLAY_DEVICE_PROFILE
#define DISPLAY_DEVICE_PROFILE 0x6d6e7472 // 'mntr'
#endif
// D50 adapted color primaries of the internal sRGB color profile.
static s15Fixed16Number sRGB_reference[3][3] = {
{ 0x06fa0, 0x06296, 0x024a0 }, // ( 0.436035, 0.385101, 0.143066 )
{ 0x038f2, 0x0b789, 0x00f85 }, // ( 0.222443, 0.716934, 0.060623 )
{ 0x0038f, 0x018da, 0x0b6c4 }, // ( 0.013901, 0.097076, 0.713928 )
};
// Reference media white point of the sRGB IEC61966-2.1 color profile.
static struct XYZNumber D65 = {
0xf351, 0x10000, 0x116cc // ( 0.950455, 1.000000, 1.089050 )
};
static void check_profile_description(qcms_profile *profile)
{
printf("Test profile description:\n");
const char* description = qcms_profile_get_description(profile);
printf("description=[%s]\n\n", description);
}
static void check_profile_pcs_white_point(const qcms_profile *profile)
{
float rX = s15Fixed16Number_to_float(profile->redColorant.X);
float gX = s15Fixed16Number_to_float(profile->greenColorant.X);
float bX = s15Fixed16Number_to_float(profile->blueColorant.X);
float rY = s15Fixed16Number_to_float(profile->redColorant.Y);
float gY = s15Fixed16Number_to_float(profile->greenColorant.Y);
float bY = s15Fixed16Number_to_float(profile->blueColorant.Y);
float rZ = s15Fixed16Number_to_float(profile->redColorant.Z);
float gZ = s15Fixed16Number_to_float(profile->greenColorant.Z);
float bZ = s15Fixed16Number_to_float(profile->blueColorant.Z);
printf("Test PCS white point against expected D50 XYZ values\n");
float X = rX + gX + bX;
float Y = rY + gY + bY;
float Z = rZ + gZ + bZ;
float x = X / (X + Y + Z);
float y = Y / (X + Y + Z);
printf("Computed profile D50 White point xyY = [%.6f %.6f %.6f]\n", x, y, Y);
float xerr = x - 0.345702915; // Compute error to ICC spec D50 xyY.
float yerr = y - 0.358538597;
float Yerr = Y - 1.000000000;
printf("D50 white point error = %.6f\n\n", (float)
sqrt((xerr * xerr) + (yerr * yerr) + (Yerr * Yerr)));
}
static void check_profile_media_white_point(const qcms_profile *profile)
{
int errX = profile->mediaWhitePoint.X - D65.X;
int errY = profile->mediaWhitePoint.Y - D65.Y;
int errZ = profile->mediaWhitePoint.Z - D65.Z;
printf("Test media white point against expected D65 XYZ values\n");
printf("Internal profile D65 values = [0x%X, 0x%X, 0x%X]\n",
profile->mediaWhitePoint.X, profile->mediaWhitePoint.Y, profile->mediaWhitePoint.Z);
printf("D65 media white point error = [%d, %d, %d]\n\n", errX, errY, errZ);
}
static s15Fixed16Number check_profile_primaries(const qcms_profile *profile)
{
s15Fixed16Number sRGB_internal[3][3];
s15Fixed16Number primary_error;
int i, j;
printf("Test qcms internal sRGB color primaries\n");
sRGB_internal[0][0] = profile->redColorant.X;
sRGB_internal[1][0] = profile->redColorant.Y;
sRGB_internal[2][0] = profile->redColorant.Z;
sRGB_internal[0][1] = profile->greenColorant.X;
sRGB_internal[1][1] = profile->greenColorant.Y;
sRGB_internal[2][1] = profile->greenColorant.Z;
sRGB_internal[0][2] = profile->blueColorant.X;
sRGB_internal[1][2] = profile->blueColorant.Y;
sRGB_internal[2][2] = profile->blueColorant.Z;
primary_error = 0;
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
s15Fixed16Number tmp = sRGB_internal[i][j] - sRGB_reference[i][j];
printf(" %d", tmp);
primary_error += abs(tmp);
}
printf("\n");
}
return primary_error;
}
static int qcms_test_internal_srgb(size_t width,
size_t height,
int iterations,
const char *in_path,
const char *out_path,
const int force_software)
{
s15Fixed16Number primary_error;
qcms_profile *profile = qcms_profile_sRGB();
assert(profile->class == DISPLAY_DEVICE_PROFILE);
assert(profile->rendering_intent == QCMS_INTENT_PERCEPTUAL);
assert(profile->color_space == RGB_SIGNATURE);
assert(profile->pcs == XYZ_SIGNATURE);
if (qcms_profile_is_bogus(profile)) {
fprintf(stderr, "Failure: the internal sRGB profile failed the bogus profile check\n");
qcms_profile_release(profile);
return -1;
}
// Compute tristimulus matrix error.
primary_error = check_profile_primaries(profile);
printf("Total primary error = 0x%x [%.6f]\n\n", primary_error, primary_error / 65536.0);
// Verify media white point correctness.
check_profile_media_white_point(profile);
// Verify PCS white point correctness.
check_profile_pcs_white_point(profile);
// Output profile description.
check_profile_description(profile);
qcms_profile_release(profile);
return primary_error;
}
struct qcms_test_case qcms_test_internal_srgb_info = {
"qcms_test_internal_srgb",
qcms_test_internal_srgb,
QCMS_TEST_DISABLED
};

View File

@ -0,0 +1,131 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcms.h"
#include "qcms_test_util.h"
#include "timing.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Manually update the items below to add more tests.
extern struct qcms_test_case qcms_test_tetra_clut_rgba_info;
extern struct qcms_test_case qcms_test_munsell_info;
extern struct qcms_test_case qcms_test_internal_srgb_info;
extern struct qcms_test_case qcms_test_ntsc_gamut_info;
extern struct qcms_test_case qcms_test_output_trc_info;
struct qcms_test_case qcms_test[5];
#define TEST_CASES (sizeof(qcms_test) / sizeof(qcms_test[0]))
static void initialize_tests()
{
qcms_test[0] = qcms_test_tetra_clut_rgba_info;
qcms_test[1] = qcms_test_munsell_info;
qcms_test[2] = qcms_test_internal_srgb_info;
qcms_test[3] = qcms_test_ntsc_gamut_info;
qcms_test[4] = qcms_test_output_trc_info;
}
static void list_tests()
{
int i;
printf("Available qcms tests:\n");
for (i = 0; i < TEST_CASES; ++i) {
printf("\t%s\n", qcms_test[i].test_name);
}
exit(EXIT_FAILURE);
}
static void print_usage()
{
printf("Usage:\n\tqcms_test -w WIDTH -h HEIGHT -n ITERATIONS -t TEST\n");
printf("\t-w INT\t\ttest image width\n");
printf("\t-h INT\t\ttest image height\n");
printf("\t-n INT\t\tnumber of iterations for each test\n");
printf("\t-a\t\trun all tests\n");
printf("\t-l\t\tlist available tests\n");
printf("\t-s \t\tforce software(non-sse) transform function, where available\n");
printf("\t-i STRING\tspecify input icc color profile\n");
printf("\t-o STRING\tspecify output icc color profile\n");
printf("\t-t STRING\trun specific test - use \"-l\" to list possible values\n");
printf("\n");
exit(1);
}
int enable_test(const char *args)
{
int i;
if (!args)
return 0;
for (i = 0; i < TEST_CASES; ++i) {
if (strcmp(qcms_test[i].test_name, args) == 0) {
qcms_test[i].status = QCMS_TEST_ENABLED;
return 1;
}
}
return 0;
}
int main(int argc, const char **argv)
{
int iterations = 1;
size_t height = 2000;
size_t width = 2000;
int run_all = 0;
const char *in = NULL, *out = NULL;
int force_software = 0;
int exit_status;
int enabled_tests = 0;
int i;
initialize_tests();
seconds();
if (argc == 1) {
print_usage();
}
while (argc > 1) {
if (strcmp(argv[1], "-n") == 0)
iterations = abs(atoi(argv[2]));
else if (strcmp(argv[1], "-w") == 0)
width = (size_t) abs(atoi(argv[2]));
else if (strcmp(argv[1], "-h") == 0)
height = (size_t) abs(atoi(argv[2]));
else if (strcmp(argv[1], "-l") == 0)
list_tests();
else if (strcmp(argv[1], "-t") == 0)
enabled_tests += enable_test(argv[2]);
else if (strcmp(argv[1], "-a") == 0)
run_all = 1;
else if (strcmp(argv[1], "-i") == 0)
in = argv[2];
else if (strcmp(argv[1], "-o") == 0)
out = argv[2];
else if (strcmp(argv[1], "-s") == 0)
force_software = 1;
(--argc, ++argv);
}
if (!run_all && !enabled_tests) {
print_usage();
}
exit_status = 0;
for (i = 0; i < TEST_CASES; ++i) {
if (run_all || QCMS_TEST_ENABLED == qcms_test[i].status)
exit_status += qcms_test[i].test_fn(width, height, iterations, in, out, force_software);
}
return exit_status;
}

View File

@ -0,0 +1,217 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcms.h"
#include "qcms_test_util.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct color_checker_chart {
char* name;
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};
struct color_checker_chart adobe_munsell[24] = {
{ "Dark Skin", 106, 81, 67, 255 },
{ "Light Skin", 182, 149, 130, 255 },
{ "Blue Sky", 103, 122, 154, 255 },
{ "Foliage", 95, 108, 69, 255 },
{ "Blue Flower", 129, 128, 174, 255 },
{ "Bluish Green", 133, 189, 170, 255 },
{ "Orange", 194, 121, 48, 255 },
{ "Purplish Blue", 79, 91, 162, 255 },
{ "Moderate Red", 170, 85, 97, 255 },
{ "Purple", 84, 62, 105, 255 },
{ "Yellow Green", 167, 186, 73, 255 },
{ "Orange Yellow", 213, 162, 57, 255 },
{ "Blue", 54, 62, 149, 255 },
{ "Green", 101, 148, 76, 255 },
{ "Red", 152, 48, 58, 255 },
{ "Yellow", 228, 199, 55, 255 },
{ "Magenta", 164, 83, 144, 255 },
{ "Cyan", 63, 134, 163, 255 },
{ "White", 242, 241, 236, 255 },
{ "Neutral 8", 200, 200, 199, 255 },
{ "Neutral 6.5", 159, 160, 159, 255 },
{ "Neutral 5", 122, 121, 120, 255 },
{ "Neutral 3.5", 84, 84, 84, 255 },
{ "Black", 53, 53, 53, 255 },
};
struct color_checker_chart srgb_munsell[24] = {
{ "Dark Skin", 115, 80, 64, 255 },
{ "Light Skin", 195, 151, 130, 255 },
{ "Blue Sky", 94, 123, 156, 255 },
{ "Foliage", 88, 108, 65, 255 },
{ "Blue Flower", 130, 129, 177, 255 },
{ "Bluish Green", 100, 190, 171, 255 },
{ "Orange", 217, 122, 37, 255 },
{ "Purplish Blue", 72, 91, 165, 255 },
{ "Moderate Red", 194, 84, 98, 255 },
{ "Purple", 91, 59, 107, 255 },
{ "Yellow Green", 160, 188, 60, 255 },
{ "Orange Yellow", 230, 163, 42, 255 },
{ "Blue", 46, 60, 153, 255 },
{ "Green", 71, 150, 69, 255 },
{ "Red", 177, 44, 56, 255 },
{ "Yellow", 238, 200, 27, 255 },
{ "Magenta", 187, 82, 148, 255 },
{ "Cyan", /* -49 */ 0, 135, 166, 255 },
{ "White", 243, 242, 237, 255 },
{ "Neutral 8",201, 201, 201, 255 },
{ "Neutral 6.5", 161, 161, 161, 255 },
{ "Neutral 5",122, 122, 121, 255 },
{ "Neutral 3.5", 83, 83, 83, 255 },
{ "Black", 50, 49, 50, 255 },
};
extern void qcms_transform_data_rgba_out_lut_precache(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format);
static qcms_bool invalid_rgb_color_profile(qcms_profile *profile)
{
return rgbData != qcms_profile_get_color_space(profile) || qcms_profile_is_bogus(profile);
}
static int color_error(struct color_checker_chart cx, struct color_checker_chart cy)
{
int dr = cx.r - cy.r;
int dg = cx.g - cy.g;
int db = cx.b - cy.b;
return round(sqrt((dr * dr) + (dg * dg) + (db * db)));
}
static qcms_profile* open_profile_from_path(const char *path)
{
if (strcmp(path, "internal-srgb") != 0)
return qcms_profile_from_path(path);
return qcms_profile_sRGB();
}
static int qcms_test_munsell(size_t width,
size_t height,
int iterations,
const char *in_path,
const char *out_path,
const int force_software)
{
qcms_profile *in_profile = NULL;
qcms_profile *out_profile = NULL;
qcms_format_type format = {0, 2}; // RGBA
qcms_transform *transform;
struct color_checker_chart *source_munsell = NULL;
struct color_checker_chart *reference_munsell = NULL;
struct color_checker_chart destination_munsell[24];
char file_name[256];
FILE *output;
int dE[24];
float rmse;
int i;
printf("Test qcms data transform accuracy using Munsell colors\n");
fflush(stdout);
if (in_path == NULL || out_path == NULL) {
fprintf(stderr, "%s: please provide valid ICC profiles via -i/o options\n", __FUNCTION__);
return EXIT_FAILURE;
}
in_profile = open_profile_from_path(in_path);
if (!in_profile || invalid_rgb_color_profile(in_profile)) {
fprintf(stderr, "Invalid input profile\n");
return EXIT_FAILURE;
}
source_munsell = srgb_munsell;
if (strstr(in_profile->description, "Adobe") != NULL) {
source_munsell = adobe_munsell;
}
printf("Input profile %s\n", in_profile->description);
out_profile = open_profile_from_path(out_path);
if (!out_profile || invalid_rgb_color_profile(out_profile)) {
fprintf(stderr, "Invalid output profile\n");
return EXIT_FAILURE;
}
reference_munsell = srgb_munsell;
if (strstr(out_profile->description, "Adobe") != NULL) {
reference_munsell = adobe_munsell;
}
printf("Output profile %s (using qcms precache)\n", out_profile->description);
qcms_profile_precache_output_transform(out_profile);
transform = qcms_transform_create(in_profile, QCMS_DATA_RGBA_8, out_profile, QCMS_DATA_RGBA_8, QCMS_INTENT_DEFAULT);
if (!transform) {
fprintf(stderr, "Failed to create color transform\n");
return EXIT_FAILURE;
} else if (force_software) {
transform->transform_fn = qcms_transform_data_rgba_out_lut_precache;
}
if (qcms_profile_match(in_profile, out_profile)) {
printf("Note: input / output profiles match\n");
}
rmse = 0.0f;
for (i = 0; i < 24; i++) {
transform->transform_fn(transform, &source_munsell[i].r, &destination_munsell[i].r, 1, format);
dE[i] = color_error(reference_munsell[i], destination_munsell[i]);
rmse += dE[i] * dE[i];
}
rmse = sqrt(rmse / 24);
printf("RMS color error %.2f\n", rmse);
// Name and open test result file.
sprintf(file_name, "qcms-test-%ld-munsell-%s-to-%s-rms-%.3f.csv", (long int)time(NULL), in_profile->description, out_profile->description, rmse);
// FIXME: remove spaces from the file name?
output = fopen(file_name, "w");
// Print headers.
if (force_software)
fprintf(output, "Report for: qcms_transform_data_rgba_out_lut_precache\n\n");
else
fprintf(output, "Report for: qcms_transform_data_rgba_out_lut_sse2\n\n");
fprintf(output, "%14s,\t%s,\t%s,\t%s\n\n", "Color,", "Actual,,", "Expected,", "dE");
// Print results.
for (i = 0; i < 24; i++) {
fprintf(output, "%14s,\t%d,%d,%d,\t%d,%d,%d,\t%d\n",
source_munsell[i].name,
destination_munsell[i].r, destination_munsell[i].g, destination_munsell[i].b,
reference_munsell[i].r, reference_munsell[i].g, reference_munsell[i].b,
dE[i]);
}
fprintf(output, "\nRMS color error = %.2f\n", rmse);
fclose(output);
printf("Output written to %s\n", file_name);
return rmse > 0.000001f;
}
struct qcms_test_case qcms_test_munsell_info = {
"qcms_test_munsell",
qcms_test_munsell,
QCMS_TEST_DISABLED
};

View File

@ -0,0 +1,74 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcms_test_util.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static qcms_bool invalid_rgb_color_profile(qcms_profile *profile)
{
return rgbData != qcms_profile_get_color_space(profile) || qcms_profile_is_bogus(profile);
}
static int qcms_test_ntsc_gamut(size_t width,
size_t height,
int iterations,
const char *input_path,
const char *referece_path,
const int force_software)
{
qcms_profile *input_profile;
qcms_profile *reference_profile = qcms_profile_sRGB();
qcms_transform *transform;
float input_gamut_metric, reference_gamut_metric;
if (!input_path) {
fprintf(stderr, "%s: please provide valid ICC profiles via -i/o options\n", __FUNCTION__);
return EXIT_FAILURE;
}
input_profile = qcms_profile_from_path(input_path);
if (!input_profile || invalid_rgb_color_profile(input_profile)) {
fprintf(stderr, "Invalid input profile\n");
return EXIT_FAILURE;
}
transform = qcms_transform_create(input_profile, QCMS_DATA_RGBA_8, reference_profile, QCMS_DATA_RGBA_8, QCMS_INTENT_DEFAULT);
if (!transform) {
fprintf(stderr, "Could not create transform\n");
return EXIT_FAILURE;
}
if (!(transform->transform_flags & TRANSFORM_FLAG_MATRIX)) {
fprintf(stderr, "Transform is not matrix\n");
qcms_transform_release(transform);
qcms_profile_release(input_profile);
qcms_profile_release(reference_profile);
return EXIT_FAILURE;
}
printf("NTSC 1953 relative gamut area test\n");
input_gamut_metric = qcms_profile_ntsc_relative_gamut_size(input_profile);
printf("Input profile\n\tDescription: %s\n\tNTSC relative gamut area: %.3f %%\n",
input_profile->description, input_gamut_metric);
reference_gamut_metric = qcms_profile_ntsc_relative_gamut_size(reference_profile);
printf("Internal reference profile\n\tDescription: %s\n\tNTSC relative gamut area: %.3f %%\n",
reference_profile->description, reference_gamut_metric);
qcms_transform_release(transform);
qcms_profile_release(input_profile);
qcms_profile_release(reference_profile);
return 0;
}
struct qcms_test_case qcms_test_ntsc_gamut_info = {
"qcms_test_ntsc_gamut",
qcms_test_ntsc_gamut,
QCMS_TEST_DISABLED
};

View File

@ -0,0 +1,255 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcms.h"
#include "qcms_test_util.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define PARAMETRIC_CURVE_TYPE 0x70617261 // 'para'
static const float inverse65535 = (float) (1.0 / 65535.0);
extern float clamp_float(float a);
static int get_output_gamma_table(const char *profile_path, uint16_t **table, size_t *size)
{
qcms_transform *transform;
qcms_profile *sRGB;
qcms_profile *target;
target = qcms_profile_from_path(profile_path);
if (!target) {
fprintf(stderr, "Invalid input profile\n");
return EXIT_FAILURE;
}
sRGB = qcms_profile_sRGB();
transform = qcms_transform_create(sRGB, QCMS_DATA_RGBA_8, target, QCMS_DATA_RGBA_8, QCMS_INTENT_DEFAULT);
if (!transform) {
fprintf(stderr, "Failed to create colour transform\n");
qcms_profile_release(sRGB);
qcms_profile_release(target);
return EXIT_FAILURE;
}
*size = qcms_transform_get_output_trc_rgba(transform, target, QCMS_TRC_USHORT, NULL);
assert(*size >= 256);
*table = malloc(*size * sizeof(uint16_t) * 4);
qcms_transform_get_output_trc_rgba(transform, target, QCMS_TRC_USHORT, *table);
qcms_transform_release(transform);
qcms_profile_release(sRGB);
qcms_profile_release(target);
return 0;
}
static int get_input_gamma_table(const char *profile_path, uint16_t **table, size_t *size)
{
qcms_transform *transform;
qcms_profile *source;
qcms_profile *sRGB;
source = qcms_profile_from_path(profile_path);
if (!source) {
fprintf(stderr, "Invalid input profile\n");
return EXIT_FAILURE;
}
sRGB = qcms_profile_sRGB();
transform = qcms_transform_create(source, QCMS_DATA_RGBA_8, sRGB, QCMS_DATA_RGBA_8, QCMS_INTENT_DEFAULT);
if (!transform) {
fprintf(stderr, "Failed to create colour transform\n");
qcms_profile_release(sRGB);
qcms_profile_release(source);
return EXIT_FAILURE;
}
*size = qcms_transform_get_input_trc_rgba(transform, source, QCMS_TRC_USHORT, NULL);
assert(*size >= 256);
*table = calloc(*size, sizeof(uint16_t) * 4);
qcms_transform_get_input_trc_rgba(transform, source, QCMS_TRC_USHORT, *table);
qcms_transform_release(transform);
qcms_profile_release(sRGB);
qcms_profile_release(source);
return 0;
}
static int qcms_test_output_trc(size_t width,
size_t height,
int iterations,
const char *in_path,
const char *out_path,
const int force_software)
{
uint16_t *gamma_table_out = NULL;
size_t output_size = 0;
qcms_profile *profile;
long time_stamp = (long)time(NULL);
char output_file_name[1024];
float scale_factor;
size_t i;
if (!in_path) {
fprintf(stderr, "%s: please provide valid ICC profiles via -i option\n", __FUNCTION__);
return EXIT_FAILURE;
}
printf("Test color profile gamma curves\n");
fflush(stdout);
if (get_output_gamma_table(in_path, &gamma_table_out, &output_size) != 0) {
fprintf(stderr, "Unable to extract output gamma table\n");
return EXIT_FAILURE;
}
printf("Output gamma table size = %zu\n", output_size);
profile = qcms_profile_from_path(in_path);
if (!profile) {
fprintf(stderr, "Invalid input profile\n");
free(gamma_table_out);
return EXIT_FAILURE;
}
if (profile->redTRC->type == PARAMETRIC_CURVE_TYPE) {
// Check the red TRC curve only for now.
int type = - (int)(profile->redTRC->count + 1);
uint16_t *gamma_table_in = NULL;
size_t input_size = 0;
FILE *output_file;
printf("Detected parametric curve type = %d\n", profile->redTRC->count);
if (get_input_gamma_table(in_path, &gamma_table_in, &input_size) != 0) {
fprintf(stderr, "Failed to compute input gamma table\n");
qcms_profile_release(profile);
free(gamma_table_out);
return EXIT_FAILURE;
}
// Write output to stdout and tables into a csv file.
sprintf(output_file_name, "qcms-test-%ld-parametric-gamma-output-%s.csv",
time_stamp, profile->description);
printf("Writing output gamma tables to %s\n", output_file_name);
output_file = fopen(output_file_name, "w");
printf("Parametric gamma values for profile %s description [%s]\n",
in_path, profile->description);
fprintf(output_file, "Parametric gamma values for profile %s description [%s]\n",
in_path, profile->description);
printf("gamma = %.6f, a = %.6f, b = %.6f, c = %.6f, d = %.6f, e = %.6f, f = %.6f\n",
profile->redTRC->parameter[0], profile->redTRC->parameter[1], profile->redTRC->parameter[2],
profile->redTRC->parameter[3], profile->redTRC->parameter[4], profile->redTRC->parameter[5],
profile->redTRC->parameter[6]);
fprintf(output_file, "gamma, a, b, c, d, e, f\n");
fprintf(output_file, "%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f\n",
profile->redTRC->parameter[0], profile->redTRC->parameter[1], profile->redTRC->parameter[2],
profile->redTRC->parameter[3], profile->redTRC->parameter[4], profile->redTRC->parameter[5],
profile->redTRC->parameter[6]);
fprintf(output_file, "\nInput curve size: %zu", input_size);
fprintf(output_file, "\nOutput curve size: %zu", output_size);
fprintf(output_file, "\n\nInput gamma, Output gamma, LCMS Output gamma, Output gamma error\n");
// Output gamma curve down-sample factor.
scale_factor = (float)(output_size - 1) / (input_size - 1);
for (i = 0; i < input_size; ++i) {
float input = gamma_table_in[i * 4] * inverse65535;
size_t out_index = (size_t)floor(i * scale_factor + 0.5);
float output = gamma_table_out[out_index * 4] * inverse65535;
float x = out_index / (float)(output_size - 1);
float reference = clamp_float(evaluate_parametric_curve(type, profile->redTRC->parameter, x));
float difference = fabs(output - reference);
fprintf(output_file, "%.6f, %.6f, %6f, %6f\n", input, output, reference, difference);
}
fprintf(output_file, "\nNote: the output gamma curves are down-sampled by a factor of %zu / %zu\n",
output_size, input_size);
fclose(output_file);
free(gamma_table_in);
} else {
uint16_t *gamma_table_in = NULL;
size_t input_size = 0;
FILE *output_file;
if (get_input_gamma_table(in_path, &gamma_table_in, &input_size) != 0) {
fprintf(stderr, "Failed to compute input gamma table\n");
qcms_profile_release(profile);
free(gamma_table_out);
return EXIT_FAILURE;
}
// Write output to stdout and tables into a csv file.
sprintf(output_file_name, "qcms-test-%ld-gamma-output-%s.csv",
time_stamp, profile->description);
printf("Writing gamma tables to %s\n", output_file_name);
output_file = fopen(output_file_name, "w");
printf("Gamma values for profile %s description [%s]\n",
in_path, profile->description);
fprintf(output_file, "Gamma values for profile %s description [%s]\n",
in_path, profile->description);
if (profile->redTRC->count == 0) {
printf("Gamma LUT type 0: linear gamma\n");
fprintf(output_file, "Gamma LUT type 0: linear gamma\n");
} else if (profile->redTRC->count == 1) {
float gamma = profile->redTRC->data[0] / 256.0f;
printf("Gamma LUT type 1: gamma = %.6f\n", gamma);
fprintf(output_file, "Gamma LUT type 1: gamma = %.6f\n", gamma);
} else {
printf("Gamma LUT table size = %u\n", profile->redTRC->count);
fprintf(output_file, "Gamma LUT table size = %u\n", profile->redTRC->count);
}
fprintf(output_file, "\nInput curve size: %zu", input_size);
fprintf(output_file, "\nOutput curve size: %zu", output_size);
fprintf(output_file, "\n\nInput gamma, Output gamma\n");
// Output gamma curve down-sample factor.
scale_factor = (float)(output_size - 1) / (input_size - 1);
for (i = 0; i < input_size; ++i) {
float input = gamma_table_in[i * 4] * inverse65535;
size_t out_index = (size_t)floor(i * scale_factor + 0.5);
float output = gamma_table_out[out_index * 4] * inverse65535;
fprintf(output_file, "%.6f, %.6f\n", input, output);
}
fprintf(output_file, "\nNote: the output gamma curves are down-sampled by a factor of %zu / %zu\n",
output_size, input_size);
fclose(output_file);
free(gamma_table_in);
}
qcms_profile_release(profile);
free(gamma_table_out);
return 0;
}
struct qcms_test_case qcms_test_output_trc_info = {
"qcms_test_output_trc",
qcms_test_output_trc,
QCMS_TEST_DISABLED
};

View File

@ -0,0 +1,180 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcms.h"
#include "qcms_test_util.h"
#include "timing.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// External qcms tetra clut interpolators.
extern void qcms_transform_data_tetra_clut_rgba(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format);
#ifdef SSE2_ENABLE
extern void qcms_transform_data_tetra_clut_rgba_sse2(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format);
#else
void qcms_transform_data_tetra_clut_rgba_dummy(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format)
{
(void)(transform);
(void)(src);
(void)(dest);
(void)(length);
(void)(output_format);
}
#endif
static float *create_lut(size_t lutSize)
{
float *lut = malloc(lutSize * sizeof(float));
size_t i;
for (i = 0; i < lutSize; ++i) {
lut[i] = (rand() & 255) * (1.0f / 255.0f);
}
return lut;
}
static int diffs;
static int validate(unsigned char *dst0, unsigned char *dst1, size_t length, int limit, const size_t pixel_size)
{
size_t bytes = length * pixel_size;
size_t i;
// Compare dst0/dst0 byte-by-byte, allowing for minor differences due
// to SSE rounding modes (controlled by the limit argument).
if (limit < 0)
limit = 255; // Ignore all differences.
for (diffs = 0, i = 0; i < bytes; ++i) {
if (abs((int)dst0[i] - (int)dst1[i]) > limit) {
++diffs;
}
}
return !diffs;
}
static int qcms_test_tetra_clut_rgba(size_t width,
size_t height,
int iterations,
const char *in_profile,
const char *out_profile,
const int force_software)
{
qcms_transform transform0, transform1;
qcms_format_type format = {2, 0};
uint16_t samples = 33;
size_t lutSize;
float *lut0, *lut1;
const size_t length = width * height;
const size_t pixel_size = 4;
double time0, time1;
int i;
printf("Test qcms clut transforms for %d iterations\n", iterations);
printf("Test image size %u x %u pixels\n", (unsigned) width, (unsigned) height);
fflush(stdout);
srand(0);
seconds();
memset(&transform0, 0, sizeof(transform0));
memset(&transform1, 0, sizeof(transform1));
transform0.grid_size = samples;
transform1.grid_size = samples;
transform0.transform_flags = 0;
transform1.transform_flags = 0;
lutSize = 3 * samples * samples * samples;
lut0 = create_lut(lutSize);
lut1 = (float *)malloc(lutSize * sizeof(float));
memcpy(lut1, lut0, lutSize * sizeof(float));
transform0.r_clut = &lut0[0];
transform0.g_clut = &lut0[1];
transform0.b_clut = &lut0[2];
transform1.r_clut = &lut1[0];
transform1.g_clut = &lut1[1];
transform1.b_clut = &lut1[2];
// Re-generate and use different data sources during the iteration loop
// to avoid compiler / cache optimizations that may affect performance.
time0 = 0.0;
time1 = 0.0;
for (i = 0; i < iterations; ++i) {
unsigned char *src0 = (unsigned char *)calloc(length, pixel_size);
unsigned char *src1 = (unsigned char *)calloc(length, pixel_size);
unsigned char *dst0 = (unsigned char *)calloc(length, pixel_size);
unsigned char *dst1 = (unsigned char *)calloc(length, pixel_size);
generate_source_uint8_t(src0, length, pixel_size);
memcpy(src1, src0, length * pixel_size);
#define TRANSFORM_TEST0 qcms_transform_data_tetra_clut_rgba
#ifdef SSE2_ENABLE
#define TRANSFORM_TEST1 qcms_transform_data_tetra_clut_rgba_sse2
#else
#define TRANSFORM_TEST1 qcms_transform_data_tetra_clut_rgba_dummy
#endif
TIME(TRANSFORM_TEST0(&transform0, src0, dst0, length, format), &time0);
TIME(TRANSFORM_TEST1(&transform1, src1, dst1, length, format), &time1);
if (!validate(dst0, dst1, length, 0, pixel_size)) {
fprintf(stderr, "Invalid transform output: %d diffs\n", diffs);
}
free(src0);
free(src1);
free(dst0);
free(dst1);
}
#define STRINGIZE(s) #s
#define STRING(s) STRINGIZE(s)
printf("%.6lf (avg %.6lf) seconds " STRING(TRANSFORM_TEST0) "\n",
time0, time0 / iterations);
printf("%.6lf (avg %.6lf) seconds " STRING(TRANSFORM_TEST1) "\n",
time1, time1 / iterations);
printf("%.6lf speedup after %d iterations\n\n",
time0 / time1, iterations);
free(lut0);
free(lut1);
return diffs;
}
struct qcms_test_case qcms_test_tetra_clut_rgba_info = {
"qcms_test_tetra_clut_rgba",
qcms_test_tetra_clut_rgba,
QCMS_TEST_DISABLED
};

View File

@ -0,0 +1,280 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcms_test_util.h"
#include <math.h>
#include <stdlib.h>
#define MAX_FLOAT_ERROR 0.000001f
// Store random pixel data in the source.
void generate_source_uint8_t(unsigned char *src, const size_t length, const size_t pixel_size)
{
size_t bytes = length * pixel_size;
size_t i;
for (i = 0; i < bytes; ++i) {
*src++ = rand() & 255;
}
}
// Parametric Fn using floating point <from lcms/src/cmsgamma.c>: DefaultEvalParametricFn
float evaluate_parametric_curve(int type, const float params[], float r)
{
float e, val, disc;
switch (type) {
// X = Y ^ Gamma
case 1:
if (r < 0) {
if (fabs(params[0] - 1.0) < MAX_FLOAT_ERROR)
val = r;
else
val = 0;
}
else
val = pow(r, params[0]);
break;
// Type 1 Reversed: X = Y ^1/gamma
case -1:
if (r < 0) {
if (fabs(params[0] - 1.0) < MAX_FLOAT_ERROR)
val = r;
else
val = 0;
}
else
val = pow(r, 1/params[0]);
break;
// CIE 122-1966
// Y = (aX + b)^Gamma | X >= -b/a
// Y = 0 | else
case 2:
disc = -params[2] / params[1];
if (r >= disc ) {
e = params[1]*r + params[2];
if (e > 0)
val = pow(e, params[0]);
else
val = 0;
}
else
val = 0;
break;
// Type 2 Reversed
// X = (Y ^1/g - b) / a
case -2:
if (r < 0)
val = 0;
else
val = (pow(r, 1.0/params[0]) - params[2]) / params[1];
if (val < 0)
val = 0;
break;
// IEC 61966-3
// Y = (aX + b)^Gamma | X <= -b/a
// Y = c | else
case 3:
disc = -params[2] / params[1];
if (disc < 0)
disc = 0;
if (r >= disc) {
e = params[1]*r + params[2];
if (e > 0)
val = pow(e, params[0]) + params[3];
else
val = 0;
}
else
val = params[3];
break;
// Type 3 reversed
// X=((Y-c)^1/g - b)/a | (Y>=c)
// X=-b/a | (Y<c)
case -3:
if (r >= params[3]) {
e = r - params[3];
if (e > 0)
val = (pow(e, 1/params[0]) - params[2]) / params[1];
else
val = 0;
}
else {
val = -params[2] / params[1];
}
break;
// IEC 61966-2.1 (sRGB)
// Y = (aX + b)^Gamma | X >= d
// Y = cX | X < d
case 4:
if (r >= params[4]) {
e = params[1]*r + params[2];
if (e > 0)
val = pow(e, params[0]);
else
val = 0;
}
else
val = r * params[3];
break;
// Type 4 reversed
// X=((Y^1/g-b)/a) | Y >= (ad+b)^g
// X=Y/c | Y< (ad+b)^g
case -4:
e = params[1] * params[4] + params[2];
if (e < 0)
disc = 0;
else
disc = pow(e, params[0]);
if (r >= disc) {
val = (pow(r, 1.0/params[0]) - params[2]) / params[1];
}
else {
val = r / params[3];
}
break;
// Y = (aX + b)^Gamma + e | X >= d
// Y = cX + f | X < d
case 5:
if (r >= params[4]) {
e = params[1]*r + params[2];
if (e > 0)
val = pow(e, params[0]) + params[5];
else
val = params[5];
}
else
val = r*params[3] + params[6];
break;
// Reversed type 5
// X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e), cd+f
// X=(Y-f)/c | else
case -5:
disc = params[3] * params[4] + params[6];
if (r >= disc) {
e = r - params[5];
if (e < 0)
val = 0;
else
val = (pow(e, 1.0/params[0]) - params[2]) / params[1];
}
else {
val = (r - params[6]) / params[3];
}
break;
// Types 6,7,8 comes from segmented curves as described in ICCSpecRevision_02_11_06_Float.pdf
// Type 6 is basically identical to type 5 without d
// Y = (a * X + b) ^ Gamma + c
case 6:
e = params[1]*r + params[2];
if (e < 0)
val = params[3];
else
val = pow(e, params[0]) + params[3];
break;
// ((Y - c) ^1/Gamma - b) / a
case -6:
e = r - params[3];
if (e < 0)
val = 0;
else
val = (pow(e, 1.0/params[0]) - params[2]) / params[1];
break;
// Y = a * log (b * X^Gamma + c) + d
case 7:
e = params[2] * pow(r, params[0]) + params[3];
if (e <= 0)
val = params[4];
else
val = params[1]*log10(e) + params[4];
break;
// (Y - d) / a = log(b * X ^Gamma + c)
// pow(10, (Y-d) / a) = b * X ^Gamma + c
// pow((pow(10, (Y-d) / a) - c) / b, 1/g) = X
case -7:
val = pow((pow(10.0, (r-params[4]) / params[1]) - params[3]) / params[2], 1.0 / params[0]);
break;
//Y = a * b^(c*X+d) + e
case 8:
val = (params[0] * pow(params[1], params[2] * r + params[3]) + params[4]);
break;
// Y = (log((y-e) / a) / log(b) - d ) / c
// a=0, b=1, c=2, d=3, e=4,
case -8:
disc = r - params[4];
if (disc < 0) val = 0;
else
val = (log(disc / params[0]) / log(params[1]) - params[3]) / params[2];
break;
// S-Shaped: (1 - (1-x)^1/g)^1/g
case 108:
val = pow(1.0 - pow(1 - r, 1/params[0]), 1/params[0]);
break;
// y = (1 - (1-x)^1/g)^1/g
// y^g = (1 - (1-x)^1/g)
// 1 - y^g = (1-x)^1/g
// (1 - y^g)^g = 1 - x
// 1 - (1 - y^g)^g
case -108:
val = 1 - pow(1 - pow(r, params[0]), params[0]);
break;
default:
// Unsupported parametric curve. Should never reach here
return 0;
}
return val;
}

View File

@ -0,0 +1,27 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#include "qcmsint.h"
#include "qcmstypes.h"
typedef int (*qcms_test_function)(size_t width,
size_t height,
int iterations,
const char *in_profile,
const char *out_profile,
const int force_software);
enum QCMS_TEST_STATUS {
QCMS_TEST_DISABLED = 0,
QCMS_TEST_ENABLED = 1,
};
struct qcms_test_case {
char test_name[256];
qcms_test_function test_fn;
enum QCMS_TEST_STATUS status;
};
void generate_source_uint8_t(unsigned char *src, const size_t length, const size_t pixel_size);
float evaluate_parametric_curve(int type, const float params[], float r);

51
third_party/qcms/src/tests/timing.h vendored Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the Chromium LICENSE file.
#ifndef TESTS_TIMING_H
#define TESTS_TIMING_H
#include <assert.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <sys/time.h>
#endif
#include <time.h>
#if defined(_WIN32)
static double seconds()
{
static double clock_frequency;
static bool have_frequency;
LARGE_INTEGER qpc;
QueryPerformanceCounter(&qpc);
if (have_frequency)
return qpc.QuadPart * clock_frequency;
have_frequency = true;
QueryPerformanceFrequency(&qpc);
clock_frequency = 1.0 / (double) qpc.QuadPart;
return seconds();
}
#else
static double seconds()
{
struct timeval now;
gettimeofday(&now, 0);
return now.tv_sec + now.tv_usec * (1.0 / 1000000.0);
}
#endif
#define TIME(function, time) do { \
double start = seconds(); \
(function); \
*time += seconds() - start; \
} while (0)
#endif // TESTS_TIMING_H

458
third_party/qcms/src/transform-sse2.c vendored Normal file
View File

@ -0,0 +1,458 @@
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 2015 Intel Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <emmintrin.h>
#include "qcmsint.h"
/* pre-shuffled: just load these into XMM reg instead of load-scalar/shufps sequence */
#define FLOATSCALE (float)(PRECACHE_OUTPUT_SIZE - 1)
#define CLAMPMAXVAL 1.0f
static const ALIGN float floatScaleX4[4] =
{ FLOATSCALE, FLOATSCALE, FLOATSCALE, FLOATSCALE};
static const ALIGN float clampMaxValueX4[4] =
{ CLAMPMAXVAL, CLAMPMAXVAL, CLAMPMAXVAL, CLAMPMAXVAL};
void qcms_transform_data_rgb_out_lut_sse2(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format)
{
unsigned int i;
float (*mat)[4] = transform->matrix;
char input_back[32];
/* Ensure we have a buffer that's 16 byte aligned regardless of the original
* stack alignment. We can't use __attribute__((aligned(16))) or __declspec(align(32))
* because they don't work on stack variables. gcc 4.4 does do the right thing
* on x86 but that's too new for us right now. For more info: gcc bug #16660 */
float const * input = (float*)(((uintptr_t)&input_back[16]) & ~0xf);
/* share input and output locations to save having to keep the
* locations in separate registers */
uint32_t const * output = (uint32_t*)input;
/* deref *transform now to avoid it in loop */
const float *igtbl_r = transform->input_gamma_table_r;
const float *igtbl_g = transform->input_gamma_table_g;
const float *igtbl_b = transform->input_gamma_table_b;
/* deref *transform now to avoid it in loop */
const uint8_t *otdata_r = &transform->output_table_r->data[0];
const uint8_t *otdata_g = &transform->output_table_g->data[0];
const uint8_t *otdata_b = &transform->output_table_b->data[0];
/* input matrix values never change */
const __m128 mat0 = _mm_load_ps(mat[0]);
const __m128 mat1 = _mm_load_ps(mat[1]);
const __m128 mat2 = _mm_load_ps(mat[2]);
/* these values don't change, either */
const __m128 max = _mm_load_ps(clampMaxValueX4);
const __m128 min = _mm_setzero_ps();
const __m128 scale = _mm_load_ps(floatScaleX4);
/* working variables */
__m128 vec_r, vec_g, vec_b, result;
const int r_out = output_format.r;
const int b_out = output_format.b;
/* CYA */
if (!length)
return;
/* one pixel is handled outside of the loop */
length--;
/* setup for transforming 1st pixel */
vec_r = _mm_load_ss(&igtbl_r[src[0]]);
vec_g = _mm_load_ss(&igtbl_g[src[1]]);
vec_b = _mm_load_ss(&igtbl_b[src[2]]);
src += 3;
/* transform all but final pixel */
for (i=0; i<length; i++)
{
/* position values from gamma tables */
vec_r = _mm_shuffle_ps(vec_r, vec_r, 0);
vec_g = _mm_shuffle_ps(vec_g, vec_g, 0);
vec_b = _mm_shuffle_ps(vec_b, vec_b, 0);
/* gamma * matrix */
vec_r = _mm_mul_ps(vec_r, mat0);
vec_g = _mm_mul_ps(vec_g, mat1);
vec_b = _mm_mul_ps(vec_b, mat2);
/* crunch, crunch, crunch */
vec_r = _mm_add_ps(vec_g, _mm_add_ps(vec_r, vec_b));
vec_r = _mm_max_ps(min, vec_r);
vec_r = _mm_min_ps(max, vec_r);
result = _mm_mul_ps(vec_r, scale);
/* store calc'd output tables indices */
_mm_store_si128((__m128i*)output, _mm_cvtps_epi32(result));
/* load for next loop while store completes */
vec_r = _mm_load_ss(&igtbl_r[src[0]]);
vec_g = _mm_load_ss(&igtbl_g[src[1]]);
vec_b = _mm_load_ss(&igtbl_b[src[2]]);
src += 3;
/* use calc'd indices to output RGB values */
dest[r_out] = otdata_r[output[0]];
dest[1] = otdata_g[output[1]];
dest[b_out] = otdata_b[output[2]];
dest += 3;
}
/* handle final (maybe only) pixel */
vec_r = _mm_shuffle_ps(vec_r, vec_r, 0);
vec_g = _mm_shuffle_ps(vec_g, vec_g, 0);
vec_b = _mm_shuffle_ps(vec_b, vec_b, 0);
vec_r = _mm_mul_ps(vec_r, mat0);
vec_g = _mm_mul_ps(vec_g, mat1);
vec_b = _mm_mul_ps(vec_b, mat2);
vec_r = _mm_add_ps(vec_g, _mm_add_ps(vec_r, vec_b));
vec_r = _mm_max_ps(min, vec_r);
vec_r = _mm_min_ps(max, vec_r);
result = _mm_mul_ps(vec_r, scale);
_mm_store_si128((__m128i*)output, _mm_cvtps_epi32(result));
dest[r_out] = otdata_r[output[0]];
dest[1] = otdata_g[output[1]];
dest[b_out] = otdata_b[output[2]];
}
void qcms_transform_data_rgba_out_lut_sse2(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format)
{
unsigned int i;
float (*mat)[4] = transform->matrix;
char input_back[32];
/* Ensure we have a buffer that's 16 byte aligned regardless of the original
* stack alignment. We can't use __attribute__((aligned(16))) or __declspec(align(32))
* because they don't work on stack variables. gcc 4.4 does do the right thing
* on x86 but that's too new for us right now. For more info: gcc bug #16660 */
float const * input = (float*)(((uintptr_t)&input_back[16]) & ~0xf);
/* share input and output locations to save having to keep the
* locations in separate registers */
uint32_t const * output = (uint32_t*)input;
/* deref *transform now to avoid it in loop */
const float *igtbl_r = transform->input_gamma_table_r;
const float *igtbl_g = transform->input_gamma_table_g;
const float *igtbl_b = transform->input_gamma_table_b;
/* deref *transform now to avoid it in loop */
const uint8_t *otdata_r = &transform->output_table_r->data[0];
const uint8_t *otdata_g = &transform->output_table_g->data[0];
const uint8_t *otdata_b = &transform->output_table_b->data[0];
/* input matrix values never change */
const __m128 mat0 = _mm_load_ps(mat[0]);
const __m128 mat1 = _mm_load_ps(mat[1]);
const __m128 mat2 = _mm_load_ps(mat[2]);
/* these values don't change, either */
const __m128 max = _mm_load_ps(clampMaxValueX4);
const __m128 min = _mm_setzero_ps();
const __m128 scale = _mm_load_ps(floatScaleX4);
/* working variables */
__m128 vec_r, vec_g, vec_b, result;
const int r_out = output_format.r;
const int b_out = output_format.b;
unsigned char alpha;
/* CYA */
if (!length)
return;
/* one pixel is handled outside of the loop */
length--;
/* setup for transforming 1st pixel */
vec_r = _mm_load_ss(&igtbl_r[src[0]]);
vec_g = _mm_load_ss(&igtbl_g[src[1]]);
vec_b = _mm_load_ss(&igtbl_b[src[2]]);
alpha = src[3];
src += 4;
/* transform all but final pixel */
for (i=0; i<length; i++)
{
/* position values from gamma tables */
vec_r = _mm_shuffle_ps(vec_r, vec_r, 0);
vec_g = _mm_shuffle_ps(vec_g, vec_g, 0);
vec_b = _mm_shuffle_ps(vec_b, vec_b, 0);
/* gamma * matrix */
vec_r = _mm_mul_ps(vec_r, mat0);
vec_g = _mm_mul_ps(vec_g, mat1);
vec_b = _mm_mul_ps(vec_b, mat2);
/* store alpha for this pixel; load alpha for next */
dest[3] = alpha;
alpha = src[3];
/* crunch, crunch, crunch */
vec_r = _mm_add_ps(vec_g, _mm_add_ps(vec_r, vec_b));
vec_r = _mm_max_ps(min, vec_r);
vec_r = _mm_min_ps(max, vec_r);
result = _mm_mul_ps(vec_r, scale);
/* store calc'd output tables indices */
_mm_store_si128((__m128i*)output, _mm_cvtps_epi32(result));
/* load gamma values for next loop while store completes */
vec_r = _mm_load_ss(&igtbl_r[src[0]]);
vec_g = _mm_load_ss(&igtbl_g[src[1]]);
vec_b = _mm_load_ss(&igtbl_b[src[2]]);
src += 4;
/* use calc'd indices to output RGB values */
dest[r_out] = otdata_r[output[0]];
dest[1] = otdata_g[output[1]];
dest[b_out] = otdata_b[output[2]];
dest += 4;
}
/* handle final (maybe only) pixel */
vec_r = _mm_shuffle_ps(vec_r, vec_r, 0);
vec_g = _mm_shuffle_ps(vec_g, vec_g, 0);
vec_b = _mm_shuffle_ps(vec_b, vec_b, 0);
vec_r = _mm_mul_ps(vec_r, mat0);
vec_g = _mm_mul_ps(vec_g, mat1);
vec_b = _mm_mul_ps(vec_b, mat2);
dest[3] = alpha;
vec_r = _mm_add_ps(vec_g, _mm_add_ps(vec_r, vec_b));
vec_r = _mm_max_ps(min, vec_r);
vec_r = _mm_min_ps(max, vec_r);
result = _mm_mul_ps(vec_r, scale);
_mm_store_si128((__m128i*)output, _mm_cvtps_epi32(result));
dest[r_out] = otdata_r[output[0]];
dest[1] = otdata_g[output[1]];
dest[b_out] = otdata_b[output[2]];
}
static inline __m128i __mm_swizzle_epi32(__m128i value, int bgra)
{
return bgra ? _mm_shuffle_epi32(value, _MM_SHUFFLE(0, 1, 2, 3)) :
_mm_shuffle_epi32(value, _MM_SHUFFLE(0, 3, 2, 1)) ;
}
void qcms_transform_data_tetra_clut_rgba_sse2(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length,
qcms_format_type output_format)
{
const int bgra = output_format.r;
size_t i;
const int xy_len_3 = 3 * 1;
const int x_len_3 = 3 * transform->grid_size;
const int len_3 = x_len_3 * transform->grid_size;
const __m128 __255 = _mm_set1_ps(255.0f);
const __m128 __one = _mm_set1_ps(1.0f);
const __m128 __000 = _mm_setzero_ps();
const float* r_table = transform->r_clut;
const float* g_table = transform->g_clut;
const float* b_table = transform->b_clut;
int i3, i2, i1, i0;
__m128 c3;
__m128 c2;
__m128 c1;
__m128 c0;
if (!(transform->transform_flags & TRANSFORM_FLAG_CLUT_CACHE))
qcms_transform_build_clut_cache(transform);
for (i = 0; i < length; ++i) {
unsigned char in_r = *src++;
unsigned char in_g = *src++;
unsigned char in_b = *src++;
// initialize the output result with the alpha channel only
__m128i result = _mm_setr_epi32(*src++, 0, 0, 0);
// get the input point r.xyz relative to the subcube origin
float rx = transform->r_cache[in_r];
float ry = transform->r_cache[in_g];
float rz = transform->r_cache[in_b];
// load and LUT scale the subcube maximum vertex
int xn = transform->ceil_cache[in_r] * len_3;
int yn = transform->ceil_cache[in_g] * x_len_3;
int zn = transform->ceil_cache[in_b] * xy_len_3;
// load and LUT scale the subcube origin vertex
int x0 = transform->floor_cache[in_r] * len_3;
int y0 = transform->floor_cache[in_g] * x_len_3;
int z0 = transform->floor_cache[in_b] * xy_len_3;
// tetrahedral interpolate the input color r.xyz
#define TETRA_LOOKUP_CLUT(i3, i2, i1, i0) \
c0 = _mm_set_ps(b_table[i0], g_table[i0], r_table[i0], 0.f), \
c1 = _mm_set_ps(b_table[i1], g_table[i1], r_table[i1], 0.f), \
c2 = _mm_set_ps(b_table[i2], g_table[i2], r_table[i2], 0.f), \
c3 = _mm_set_ps(b_table[i3], g_table[i3], r_table[i3], 0.f)
i0 = x0 + y0 + z0;
if (rx >= ry) {
if (ry >= rz) { // rx >= ry && ry >= rz
i3 = yn + (i1 = xn);
i1 += i0 - x0;
i2 = i3 + z0;
i3 += zn;
TETRA_LOOKUP_CLUT(i3, i2, i1, i0);
c3 = _mm_sub_ps(c3, c2);
c2 = _mm_sub_ps(c2, c1);
c1 = _mm_sub_ps(c1, c0);
} else if (rx >= rz) { // rx >= rz && rz >= ry
i3 = zn + (i1 = xn);
i1 += i0 - x0;
i2 = i3 + yn;
i3 += y0;
TETRA_LOOKUP_CLUT(i3, i2, i1, i0);
c2 = _mm_sub_ps(c2, c3);
c3 = _mm_sub_ps(c3, c1);
c1 = _mm_sub_ps(c1, c0);
} else { // rz > rx && rx >= ry
i2 = xn + (i3 = zn);
i3 += i0 - z0;
i1 = i2 + y0;
i2 += yn;
TETRA_LOOKUP_CLUT(i3, i2, i1, i0);
c2 = _mm_sub_ps(c2, c1);
c1 = _mm_sub_ps(c1, c3);
c3 = _mm_sub_ps(c3, c0);
}
} else {
if (rx >= rz) { // ry > rx && rx >= rz
i3 = xn + (i2 = yn);
i2 += i0 - y0;
i1 = i3 + z0;
i3 += zn;
TETRA_LOOKUP_CLUT(i3, i2, i1, i0);
c3 = _mm_sub_ps(c3, c1);
c1 = _mm_sub_ps(c1, c2);
c2 = _mm_sub_ps(c2, c0);
} else if (ry >= rz) { // ry >= rz && rz > rx
i3 = zn + (i2 = yn);
i2 += i0 - y0;
i1 = i3 + xn;
i3 += x0;
TETRA_LOOKUP_CLUT(i3, i2, i1, i0);
c1 = _mm_sub_ps(c1, c3);
c3 = _mm_sub_ps(c3, c2);
c2 = _mm_sub_ps(c2, c0);
} else { // rz > ry && ry > rx
i2 = yn + (i3 = zn);
i3 += i0 - z0;
i1 = i2 + xn;
i2 += x0;
TETRA_LOOKUP_CLUT(i3, i2, i1, i0);
c1 = _mm_sub_ps(c1, c2);
c2 = _mm_sub_ps(c2, c3);
c3 = _mm_sub_ps(c3, c0);
}
}
// output.xyz = column_matrix(c1, c2, c3) x r.xyz + c0.xyz
c0 = _mm_add_ps(c0, _mm_mul_ps(c1, _mm_set1_ps(rx)));
c0 = _mm_add_ps(c0, _mm_mul_ps(c2, _mm_set1_ps(ry)));
c0 = _mm_add_ps(c0, _mm_mul_ps(c3, _mm_set1_ps(rz)));
// clamp to [0.0..1.0], then scale by 255
c0 = _mm_max_ps(c0, __000);
c0 = _mm_min_ps(c0, __one);
c0 = _mm_mul_ps(c0, __255);
// int(c0) with float rounding, add alpha
result = _mm_add_epi32(result, _mm_cvtps_epi32(c0));
// swizzle and repack in result low bytes
result = __mm_swizzle_epi32(result, bgra);
result = _mm_packus_epi16(result, result);
result = _mm_packus_epi16(result, result);
// store into uint32_t* pixel destination
*(uint32_t *)dest = _mm_cvtsi128_si32(result);
dest += 4;
}
}

1641
third_party/qcms/src/transform.c vendored Normal file

File diff suppressed because it is too large Load Diff

639
third_party/qcms/src/transform_util.c vendored Normal file
View File

@ -0,0 +1,639 @@
// qcms
// Copyright (C) 2009 Mozilla Foundation
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define _ISOC99_SOURCE /* for INFINITY */
#include <math.h>
#include <assert.h>
#include <string.h> //memcpy
#include "qcmsint.h"
#include "transform_util.h"
#include "matrix.h"
#if !defined(INFINITY)
#define INFINITY HUGE_VAL
#endif
#define PARAMETRIC_CURVE_TYPE 0x70617261 //'para'
/* value must be a value between 0 and 1 */
//XXX: is the above a good restriction to have?
// the output range of this function is 0..1
float lut_interp_linear(double input_value, uint16_t *table, size_t length)
{
int upper, lower;
float value;
input_value = input_value * (length - 1); // scale to length of the array
upper = ceil(input_value);
lower = floor(input_value);
//XXX: can we be more performant here?
value = table[upper]*(1. - (upper - input_value)) + table[lower]*(upper - input_value);
/* scale the value */
return value * (1.f/65535.f);
}
/* same as above but takes and returns a uint16_t value representing a range from 0..1 */
uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, size_t length)
{
/* Start scaling input_value to the length of the array: 65535*(length-1).
* We'll divide out the 65535 next */
uintptr_t value = (input_value * (length - 1));
uint32_t upper = (value + 65534) / 65535; /* equivalent to ceil(value/65535) */
uint32_t lower = value / 65535; /* equivalent to floor(value/65535) */
/* interp is the distance from upper to value scaled to 0..65535 */
uint32_t interp = value % 65535;
value = (table[upper]*(interp) + table[lower]*(65535 - interp))/65535; // 0..65535*65535
return value;
}
/* same as above but takes an input_value from 0..PRECACHE_OUTPUT_MAX
* and returns a uint8_t value representing a range from 0..1 */
static
uint8_t lut_interp_linear_precache_output(uint32_t input_value, uint16_t *table, size_t length)
{
/* Start scaling input_value to the length of the array: PRECACHE_OUTPUT_MAX*(length-1).
* We'll divide out the PRECACHE_OUTPUT_MAX next */
uintptr_t value = (input_value * (length - 1));
/* equivalent to ceil(value/PRECACHE_OUTPUT_MAX) */
uint32_t upper = (value + PRECACHE_OUTPUT_MAX-1) / PRECACHE_OUTPUT_MAX;
/* equivalent to floor(value/PRECACHE_OUTPUT_MAX) */
uint32_t lower = value / PRECACHE_OUTPUT_MAX;
/* interp is the distance from upper to value scaled to 0..PRECACHE_OUTPUT_MAX */
uint32_t interp = value % PRECACHE_OUTPUT_MAX;
/* the table values range from 0..65535 */
value = (table[upper]*(interp) + table[lower]*(PRECACHE_OUTPUT_MAX - interp)); // 0..(65535*PRECACHE_OUTPUT_MAX)
/* round and scale */
value += (PRECACHE_OUTPUT_MAX*65535/255)/2;
value /= (PRECACHE_OUTPUT_MAX*65535/255); // scale to 0..255
return value;
}
/* value must be a value between 0 and 1 */
//XXX: is the above a good restriction to have?
float lut_interp_linear_float(float value, float *table, size_t length)
{
int upper, lower;
value = value * (length - 1);
upper = ceil(value);
lower = floor(value);
//XXX: can we be more performant here?
value = table[upper]*(1. - (upper - value)) + table[lower]*(upper - value);
/* scale the value */
return value;
}
#if 0
/* if we use a different representation i.e. one that goes from 0 to 0x1000 we can be more efficient
* because we can avoid the divisions and use a shifting instead */
/* same as above but takes and returns a uint16_t value representing a range from 0..1 */
uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length)
{
uint32_t value = (input_value * (length - 1));
uint32_t upper = (value + 4095) / 4096; /* equivalent to ceil(value/4096) */
uint32_t lower = value / 4096; /* equivalent to floor(value/4096) */
uint32_t interp = value % 4096;
value = (table[upper]*(interp) + table[lower]*(4096 - interp))/4096; // 0..4096*4096
return value;
}
#endif
void compute_curve_gamma_table_type1(float gamma_table[256], uint16_t gamma)
{
unsigned int i;
float gamma_float = u8Fixed8Number_to_float(gamma);
for (i = 0; i < 256; i++) {
// 0..1^(0..255 + 255/256) will always be between 0 and 1
gamma_table[i] = pow(i/255., gamma_float);
}
}
void compute_curve_gamma_table_type2(float gamma_table[256], uint16_t *table, size_t length)
{
unsigned int i;
for (i = 0; i < 256; i++) {
gamma_table[i] = lut_interp_linear(i/255., table, length);
}
}
void compute_curve_gamma_table_type_parametric(float gamma_table[256], float parameter[7], int count)
{
size_t X;
float interval;
float a, b, c, e, f;
float y = parameter[0];
if (count == 0) {
a = 1;
b = 0;
c = 0;
e = 0;
f = 0;
interval = -INFINITY;
} else if(count == 1) {
a = parameter[1];
b = parameter[2];
c = 0;
e = 0;
f = 0;
interval = -1 * parameter[2] / parameter[1];
} else if(count == 2) {
a = parameter[1];
b = parameter[2];
c = 0;
e = parameter[3];
f = parameter[3];
interval = -1 * parameter[2] / parameter[1];
} else if(count == 3) {
a = parameter[1];
b = parameter[2];
c = parameter[3];
e = -c;
f = 0;
interval = parameter[4];
} else if(count == 4) {
a = parameter[1];
b = parameter[2];
c = parameter[3];
e = parameter[5] - c;
f = parameter[6];
interval = parameter[4];
} else {
assert(0 && "invalid parametric function type.");
a = 1;
b = 0;
c = 0;
e = 0;
f = 0;
interval = -INFINITY;
}
for (X = 0; X < 256; X++) {
float x = X / 255.0;
if (x >= interval) {
// XXX The equations are not exactly as definied in the spec but are
// algebraic equivilent.
// TODO Should division by 255 be for the whole expression.
gamma_table[X] = clamp_float(pow(a * x + b, y) + c + e);
} else {
gamma_table[X] = clamp_float(c * x + f);
}
}
}
void compute_curve_gamma_table_type0(float gamma_table[256])
{
unsigned int i;
for (i = 0; i < 256; i++) {
gamma_table[i] = i/255.;
}
}
float clamp_float(float a)
{
/* One would naturally write this function as the following:
if (a > 1.)
return 1.;
else if (a < 0)
return 0;
else
return a;
However, that version will let NaNs pass through which is undesirable
for most consumers.
*/
if (a > 1.)
return 1.;
else if (a >= 0)
return a;
else // a < 0 or a is NaN
return 0;
}
unsigned char clamp_u8(float v)
{
if (v > 255.)
return 255;
else if (v < 0)
return 0;
else
return floor(v+.5);
}
float u8Fixed8Number_to_float(uint16_t x)
{
// 0x0000 = 0.
// 0x0100 = 1.
// 0xffff = 255 + 255/256
return x/256.;
}
/* The SSE2 code uses min & max which let NaNs pass through.
We want to try to prevent that here by ensuring that
gamma table is within expected values. */
void validate_gamma_table(float gamma_table[256])
{
int i;
for (i = 0; i < 256; i++) {
// Note: we check that the gamma is not in range
// instead of out of range so that we catch NaNs
if (!(gamma_table[i] >= 0.f && gamma_table[i] <= 1.f)) {
gamma_table[i] = 0.f;
}
}
}
float *build_input_gamma_table(struct curveType *TRC)
{
float *gamma_table;
if (!TRC) return NULL;
gamma_table = malloc(sizeof(float)*256);
if (gamma_table) {
if (TRC->type == PARAMETRIC_CURVE_TYPE) {
compute_curve_gamma_table_type_parametric(gamma_table, TRC->parameter, TRC->count);
} else {
if (TRC->count == 0) {
compute_curve_gamma_table_type0(gamma_table);
} else if (TRC->count == 1) {
compute_curve_gamma_table_type1(gamma_table, TRC->data[0]);
} else {
compute_curve_gamma_table_type2(gamma_table, TRC->data, TRC->count);
}
}
}
validate_gamma_table(gamma_table);
return gamma_table;
}
struct matrix build_colorant_matrix(qcms_profile *p)
{
struct matrix result;
result.m[0][0] = s15Fixed16Number_to_float(p->redColorant.X);
result.m[0][1] = s15Fixed16Number_to_float(p->greenColorant.X);
result.m[0][2] = s15Fixed16Number_to_float(p->blueColorant.X);
result.m[1][0] = s15Fixed16Number_to_float(p->redColorant.Y);
result.m[1][1] = s15Fixed16Number_to_float(p->greenColorant.Y);
result.m[1][2] = s15Fixed16Number_to_float(p->blueColorant.Y);
result.m[2][0] = s15Fixed16Number_to_float(p->redColorant.Z);
result.m[2][1] = s15Fixed16Number_to_float(p->greenColorant.Z);
result.m[2][2] = s15Fixed16Number_to_float(p->blueColorant.Z);
result.invalid = false;
return result;
}
/* The following code is copied nearly directly from lcms.
* I think it could be much better. For example, Argyll seems to have better code in
* icmTable_lookup_bwd and icmTable_setup_bwd. However, for now this is a quick way
* to a working solution and allows for easy comparing with lcms. */
uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int length, int NumZeroes, int NumPoles)
{
int l = 1;
int r = 0x10000;
int x = 0, res; // 'int' Give spacing for negative values
int cell0, cell1;
double val2;
double y0, y1, x0, x1;
double a, b, f;
// July/27 2001 - Expanded to handle degenerated curves with an arbitrary
// number of elements containing 0 at the beginning of the table (Zeroes)
// and another arbitrary number of poles (FFFFh) at the end.
// There are no zeros at the beginning and we are trying to find a zero, so
// return anything. It seems zero would be the less destructive choice
/* I'm not sure that this makes sense, but oh well... */
if (NumZeroes == 0 && Value == 0)
return 0;
// Does the curve belong to this case?
if (NumZeroes > 1 || NumPoles > 1)
{
int a, b, sample;
// Identify if value fall downto 0 or FFFF zone
if (Value == 0) return 0;
// if (Value == 0xFFFF) return 0xFFFF;
sample = (length-1) * ((double) Value * (1./65535.));
if (LutTable[sample] == 0xffff)
return 0xffff;
// else restrict to valid zone
a = ((NumZeroes-1) * 0xFFFF) / (length-1);
b = ((length-1 - NumPoles) * 0xFFFF) / (length-1);
l = a - 1;
r = b + 1;
// Ensure a valid binary search range
if (l < 1)
l = 1;
if (r > 0x10000)
r = 0x10000;
// If the search range is inverted due to degeneracy,
// deem LutTable non-invertible in this search range.
// Refer to https://bugzil.la/1132467
if (r <= l)
return 0;
}
// For input 0, return that to maintain black level. Note the binary search
// does not. For example, it inverts the standard sRGB gamma curve to 7 at
// the origin, causing a black level error.
if (Value == 0 && NumZeroes) {
return 0;
}
// Seems not a degenerated case... apply binary search
while (r > l) {
x = (l + r) / 2;
res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length);
if (res == Value) {
// Found exact match.
return (uint16_fract_t) (x - 1);
}
if (res > Value) r = x - 1;
else l = x + 1;
}
// Not found, should we interpolate?
// Get surrounding nodes
assert(x >= 1);
val2 = (length-1) * ((double) (x - 1) / 65535.0);
cell0 = (int) floor(val2);
cell1 = (int) ceil(val2);
assert(cell0 >= 0);
assert(cell1 >= 0);
assert(cell0 < length);
assert(cell1 < length);
if (cell0 == cell1) return (uint16_fract_t) x;
y0 = LutTable[cell0] ;
x0 = (65535.0 * cell0) / (length-1);
y1 = LutTable[cell1] ;
x1 = (65535.0 * cell1) / (length-1);
a = (y1 - y0) / (x1 - x0);
b = y0 - a * x0;
if (fabs(a) < 0.01) return (uint16_fract_t) x;
f = ((Value - b) / a);
if (f < 0.0) return (uint16_fract_t) 0;
if (f >= 65535.0) return (uint16_fract_t) 0xFFFF;
return (uint16_fract_t) floor(f + 0.5);
}
// December/16 2015 - Moved this code out of lut_inverse_interp16
// in order to save computation in invert_lut loop.
static void count_zeroes_and_poles(uint16_t *LutTable, int length, int *NumZeroes, int *NumPoles)
{
int z = 0, p = 0;
while (LutTable[z] == 0 && z < length - 1)
z++;
*NumZeroes = z;
while (LutTable[length - 1 - p] == 0xFFFF && p < length - 1)
p++;
*NumPoles = p;
}
/*
The number of entries needed to invert a lookup table should not
necessarily be the same as the original number of entries. This is
especially true of lookup tables that have a small number of entries.
For example:
Using a table like:
{0, 3104, 14263, 34802, 65535}
invert_lut will produce an inverse of:
{3, 34459, 47529, 56801, 65535}
which has an maximum error of about 9855 (pixel difference of ~38.346)
For now, we punt the decision of output size to the caller. */
static uint16_t *invert_lut(uint16_t *table, int length, size_t out_length)
{
int NumZeroes;
int NumPoles;
int i;
/* for now we invert the lut by creating a lut of size out_length
* and attempting to lookup a value for each entry using lut_inverse_interp16 */
uint16_t *output = malloc(sizeof(uint16_t)*out_length);
if (!output)
return NULL;
// December/16 2015 - Compute the input curve zero and pole extents outside
// the loop and pass them to lut_inverse_interp16.
count_zeroes_and_poles(table, length, &NumZeroes, &NumPoles);
for (i = 0; i < out_length; i++) {
double x = ((double) i * 65535.) / (double) (out_length - 1);
uint16_fract_t input = floor(x + .5);
output[i] = lut_inverse_interp16(input, table, length, NumZeroes, NumPoles);
}
return output;
}
static void compute_precache_pow(uint8_t *output, float gamma)
{
uint32_t v = 0;
for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
//XXX: don't do integer/float conversion... and round?
output[v] = 255. * pow(v/(double)PRECACHE_OUTPUT_MAX, gamma);
}
}
void compute_precache_lut(uint8_t *output, uint16_t *table, int length)
{
uint32_t v = 0;
for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
output[v] = lut_interp_linear_precache_output(v, table, length);
}
}
void compute_precache_linear(uint8_t *output)
{
uint32_t v = 0;
for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
//XXX: round?
output[v] = v / (PRECACHE_OUTPUT_SIZE/256);
}
}
qcms_bool compute_precache(struct curveType *trc, uint8_t *output)
{
if (trc->type == PARAMETRIC_CURVE_TYPE) {
float gamma_table[256];
uint16_t gamma_table_uint[256];
uint16_t i;
uint16_t *inverted;
int inverted_size = 256;
compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
for(i = 0; i < 256; i++) {
gamma_table_uint[i] = (uint16_t)(gamma_table[i] * 65535);
}
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, howeve r it is what lcms uses.
// the maximum number we would need is 65535 because that's the
// accuracy used for computing the pre cache table
if (inverted_size < 256)
inverted_size = 256;
inverted = invert_lut(gamma_table_uint, 256, inverted_size);
if (!inverted)
return false;
compute_precache_lut(output, inverted, inverted_size);
free(inverted);
} else {
if (trc->count == 0) {
compute_precache_linear(output);
} else if (trc->count == 1) {
compute_precache_pow(output, 1./u8Fixed8Number_to_float(trc->data[0]));
} else {
uint16_t *inverted;
int inverted_size = trc->count;
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, howeve r it is what lcms uses.
// the maximum number we would need is 65535 because that's the
// accuracy used for computing the pre cache table
if (inverted_size < 256)
inverted_size = 256;
inverted = invert_lut(trc->data, trc->count, inverted_size);
if (!inverted)
return false;
compute_precache_lut(output, inverted, inverted_size);
free(inverted);
}
}
return true;
}
static uint16_t *build_linear_table(int length)
{
int i;
uint16_t *output = malloc(sizeof(uint16_t)*length);
if (!output)
return NULL;
for (i = 0; i < length; i++) {
double x = ((double) i * 65535.) / (double) (length - 1);
uint16_fract_t input = floor(x + .5);
output[i] = input;
}
return output;
}
static uint16_t *build_pow_table(float gamma, int length)
{
int i;
uint16_t *output = malloc(sizeof(uint16_t)*length);
if (!output)
return NULL;
for (i = 0; i < length; i++) {
uint16_fract_t result;
double x = ((double) i) / (double) (length - 1);
x = pow(x, gamma); //XXX turn this conversion into a function
result = floor(x*65535. + .5);
output[i] = result;
}
return output;
}
void build_output_lut(struct curveType *trc,
uint16_t **output_gamma_lut, size_t *output_gamma_lut_length)
{
if (trc->type == PARAMETRIC_CURVE_TYPE) {
float gamma_table[256];
uint16_t gamma_table_uint[256];
uint16_t i;
uint16_t *inverted;
int inverted_size = 4096;
compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
for(i = 0; i < 256; i++) {
gamma_table_uint[i] = (uint16_t)(gamma_table[i] * 65535);
}
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, however it is what lcms uses.
// the maximum number we would need is 65535 because that's the
// accuracy used for computing the pre cache table
inverted = invert_lut(gamma_table_uint, 256, inverted_size);
if (!inverted)
return;
*output_gamma_lut = inverted;
*output_gamma_lut_length = inverted_size;
} else {
if (trc->count == 0) {
*output_gamma_lut = build_linear_table(4096);
*output_gamma_lut_length = 4096;
} else if (trc->count == 1) {
float gamma = 1./u8Fixed8Number_to_float(trc->data[0]);
*output_gamma_lut = build_pow_table(gamma, 4096);
*output_gamma_lut_length = 4096;
} else {
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, however it is what lcms uses.
*output_gamma_lut_length = trc->count;
if (*output_gamma_lut_length < 256)
*output_gamma_lut_length = 256;
*output_gamma_lut = invert_lut(trc->data, trc->count, *output_gamma_lut_length);
}
}
}

58
third_party/qcms/src/transform_util.h vendored Normal file
View File

@ -0,0 +1,58 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef _QCMS_TRANSFORM_UTIL_H
#define _QCMS_TRANSFORM_UTIL_H
#include <stdlib.h>
#define CLU(table,x,y,z) table[(x*len + y*x_len + z*xy_len)*3]
//XXX: could use a bettername
typedef uint16_t uint16_fract_t;
float lut_interp_linear(double input_value, uint16_t *table, size_t length);
float lut_interp_linear_float(float input_value, float *table, size_t length);
uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, size_t length);
static inline float lerp(float a, float b, float t)
{
return a*(1.f-t) + b*t;
}
unsigned char clamp_u8(float v);
float clamp_float(float a);
float u8Fixed8Number_to_float(uint16_t x);
float *build_input_gamma_table(struct curveType *TRC);
struct matrix build_colorant_matrix(qcms_profile *p);
void build_output_lut(struct curveType *trc,
uint16_t **output_gamma_lut, size_t *output_gamma_lut_length);
struct matrix matrix_invert(struct matrix mat);
qcms_bool compute_precache(struct curveType *trc, uint8_t *output);
#endif