1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/jxl/enc_image_bundle.h"
#include <jxl/cms_interface.h>
#include <atomic>
#include <limits>
#include <utility>
#include "lib/jxl/alpha.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/profiler.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/fields.h"
#include "lib/jxl/image_bundle.h"
namespace jxl {
namespace {
// Copies ib:rect, converts, and copies into out.
Status CopyToT(const ImageMetadata* metadata, const ImageBundle* ib,
const Rect& rect, const ColorEncoding& c_desired,
const JxlCmsInterface& cms, ThreadPool* pool, Image3F* out) {
PROFILER_FUNC;
ColorSpaceTransform c_transform(cms);
// Changing IsGray is probably a bug.
JXL_CHECK(ib->IsGray() == c_desired.IsGray());
bool is_gray = ib->IsGray();
if (out->xsize() < rect.xsize() || out->ysize() < rect.ysize()) {
*out = Image3F(rect.xsize(), rect.ysize());
} else {
out->ShrinkTo(rect.xsize(), rect.ysize());
}
std::atomic<bool> ok{true};
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, rect.ysize(),
[&](const size_t num_threads) {
return c_transform.Init(ib->c_current(), c_desired,
metadata->IntensityTarget(), rect.xsize(),
num_threads);
},
[&](const uint32_t y, const size_t thread) {
float* mutable_src_buf = c_transform.BufSrc(thread);
const float* src_buf = mutable_src_buf;
// Interleave input.
if (is_gray) {
src_buf = rect.ConstPlaneRow(ib->color(), 0, y);
} else if (ib->c_current().IsCMYK()) {
if (!ib->HasBlack()) {
ok.store(false);
return;
}
const float* JXL_RESTRICT row_in0 =
rect.ConstPlaneRow(ib->color(), 0, y);
const float* JXL_RESTRICT row_in1 =
rect.ConstPlaneRow(ib->color(), 1, y);
const float* JXL_RESTRICT row_in2 =
rect.ConstPlaneRow(ib->color(), 2, y);
const float* JXL_RESTRICT row_in3 = rect.ConstRow(ib->black(), y);
for (size_t x = 0; x < rect.xsize(); x++) {
// CMYK convention in JXL: 0 = max ink, 1 = white
mutable_src_buf[4 * x + 0] = row_in0[x];
mutable_src_buf[4 * x + 1] = row_in1[x];
mutable_src_buf[4 * x + 2] = row_in2[x];
mutable_src_buf[4 * x + 3] = row_in3[x];
}
} else {
const float* JXL_RESTRICT row_in0 =
rect.ConstPlaneRow(ib->color(), 0, y);
const float* JXL_RESTRICT row_in1 =
rect.ConstPlaneRow(ib->color(), 1, y);
const float* JXL_RESTRICT row_in2 =
rect.ConstPlaneRow(ib->color(), 2, y);
for (size_t x = 0; x < rect.xsize(); x++) {
mutable_src_buf[3 * x + 0] = row_in0[x];
mutable_src_buf[3 * x + 1] = row_in1[x];
mutable_src_buf[3 * x + 2] = row_in2[x];
}
}
float* JXL_RESTRICT dst_buf = c_transform.BufDst(thread);
if (!c_transform.Run(thread, src_buf, dst_buf)) {
ok.store(false);
return;
}
float* JXL_RESTRICT row_out0 = out->PlaneRow(0, y);
float* JXL_RESTRICT row_out1 = out->PlaneRow(1, y);
float* JXL_RESTRICT row_out2 = out->PlaneRow(2, y);
// De-interleave output and convert type.
if (is_gray) {
for (size_t x = 0; x < rect.xsize(); x++) {
row_out0[x] = dst_buf[x];
row_out1[x] = dst_buf[x];
row_out2[x] = dst_buf[x];
}
} else {
for (size_t x = 0; x < rect.xsize(); x++) {
row_out0[x] = dst_buf[3 * x + 0];
row_out1[x] = dst_buf[3 * x + 1];
row_out2[x] = dst_buf[3 * x + 2];
}
}
},
"Colorspace transform"));
return ok.load();
}
} // namespace
Status ImageBundle::TransformTo(const ColorEncoding& c_desired,
const JxlCmsInterface& cms, ThreadPool* pool) {
PROFILER_FUNC;
JXL_RETURN_IF_ERROR(CopyTo(Rect(color_), c_desired, cms, &color_, pool));
c_current_ = c_desired;
return true;
}
Status ImageBundle::CopyTo(const Rect& rect, const ColorEncoding& c_desired,
const JxlCmsInterface& cms, Image3F* out,
ThreadPool* pool) const {
return CopyToT(metadata_, this, rect, c_desired, cms, pool, out);
}
Status TransformIfNeeded(const ImageBundle& in, const ColorEncoding& c_desired,
const JxlCmsInterface& cms, ThreadPool* pool,
ImageBundle* store, const ImageBundle** out) {
if (in.c_current().SameColorEncoding(c_desired) && !in.HasBlack()) {
*out = ∈
return true;
}
// TODO(janwas): avoid copying via createExternal+copyBackToIO
// instead of copy+createExternal+copyBackToIO
store->SetFromImage(CopyImage(in.color()), in.c_current());
// Must at least copy the alpha channel for use by external_image.
if (in.HasExtraChannels()) {
std::vector<ImageF> extra_channels;
for (const ImageF& extra_channel : in.extra_channels()) {
extra_channels.emplace_back(CopyImage(extra_channel));
}
store->SetExtraChannels(std::move(extra_channels));
}
if (!store->TransformTo(c_desired, cms, pool)) {
return false;
}
*out = store;
return true;
}
} // namespace jxl
|