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:
parent
a62038c478
commit
0e5b249e54
@ -12,6 +12,7 @@
|
||||
'standalone_static_library': 1,
|
||||
'dependencies': [
|
||||
'core.gyp:*',
|
||||
'qcms.gyp:qcms',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../include/effects',
|
||||
|
74
gyp/qcms.gyp
Normal file
74
gyp/qcms.gyp
Normal 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
77
third_party/qcms/BUILD.gn
vendored
Normal 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
162
third_party/qcms/README.chromium
vendored
Normal 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
113
third_party/qcms/qcms.gyp
vendored
Normal 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
21
third_party/qcms/src/COPYING
vendored
Normal 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
9
third_party/qcms/src/README
vendored
Normal 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
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
30
third_party/qcms/src/chain.h
vendored
Normal 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
6
third_party/qcms/src/empty.c
vendored
Normal 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
106
third_party/qcms/src/halffloat.h
vendored
Normal 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
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
136
third_party/qcms/src/matrix.c
vendored
Normal 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
39
third_party/qcms/src/matrix.h
vendored
Normal 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
171
third_party/qcms/src/qcms.h
vendored
Normal 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
69
third_party/qcms/src/qcms_util.c
vendored
Normal 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
342
third_party/qcms/src/qcmsint.h
vendored
Normal 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
77
third_party/qcms/src/qcmstypes.h
vendored
Normal 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
16
third_party/qcms/src/tests/Makefile
vendored
Normal 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)
|
155
third_party/qcms/src/tests/qcms_test_internal_srgb.c
vendored
Normal file
155
third_party/qcms/src/tests/qcms_test_internal_srgb.c
vendored
Normal 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
|
||||
};
|
131
third_party/qcms/src/tests/qcms_test_main.c
vendored
Normal file
131
third_party/qcms/src/tests/qcms_test_main.c
vendored
Normal 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;
|
||||
}
|
217
third_party/qcms/src/tests/qcms_test_munsell.c
vendored
Normal file
217
third_party/qcms/src/tests/qcms_test_munsell.c
vendored
Normal 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
|
||||
};
|
74
third_party/qcms/src/tests/qcms_test_ntsc_gamut.c
vendored
Normal file
74
third_party/qcms/src/tests/qcms_test_ntsc_gamut.c
vendored
Normal 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
|
||||
};
|
255
third_party/qcms/src/tests/qcms_test_output_trc.c
vendored
Normal file
255
third_party/qcms/src/tests/qcms_test_output_trc.c
vendored
Normal 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
|
||||
};
|
180
third_party/qcms/src/tests/qcms_test_tetra_clut_rgba.c
vendored
Normal file
180
third_party/qcms/src/tests/qcms_test_tetra_clut_rgba.c
vendored
Normal 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
|
||||
};
|
280
third_party/qcms/src/tests/qcms_test_util.c
vendored
Normal file
280
third_party/qcms/src/tests/qcms_test_util.c
vendored
Normal 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;
|
||||
}
|
27
third_party/qcms/src/tests/qcms_test_util.h
vendored
Normal file
27
third_party/qcms/src/tests/qcms_test_util.h
vendored
Normal 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
51
third_party/qcms/src/tests/timing.h
vendored
Normal 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
458
third_party/qcms/src/transform-sse2.c
vendored
Normal 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
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
639
third_party/qcms/src/transform_util.c
vendored
Normal 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
58
third_party/qcms/src/transform_util.h
vendored
Normal 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
|
Loading…
Reference in New Issue
Block a user