Lagrange
Loading...
Searching...
No Matches
Padding.h
1/*
2 * Source: https://github.com/mkazhdan/TextureSignalProcessing/blob/master/include/Src/Padding.h
3 * SPDX-License-Identifier: MIT
4 *
5 * Copyright (c) 2018, Fabian Prada and Michael Kazhdan All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
10 * Redistributions of source code must retain the above copyright notice, this list of conditions
11 * and the following disclaimer. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * Neither the name of the Johns Hopkins University nor the names of its contributors may be used to
16 * endorse or promote products derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
25 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * This file has been modified by Adobe.
28 *
29 * All modifications are Copyright 2025 Adobe.
30 */
31#pragma once
32
33#include <lagrange/Logger.h>
34#include <lagrange/utils/span.h>
35
36// Include before any TextureSignalProcessing header to override their threadpool implementation.
37#include "ThreadPool.h"
38#define MULTI_THREADING_INCLUDED
39using namespace lagrange::texproc::threadpool;
40
41// clang-format off
42#include <lagrange/utils/warnoff.h>
43#include <Misha/RegularGrid.h>
44#include <lagrange/utils/warnon.h>
45// clang-format on
46
47namespace lagrange::texproc {
48
49// A structure for computing and (un)setting the padding needed to ensure that texture coordinates
50// fall within the rectangle defined by the _centers_ of the corner texels. Given a texture
51// coordinate (s) indexing a texture map of width W, we have:
52//. s -> W*s
53// Offsetting W -> W+D we want the associated texture coordinate t to satisfy:
54//. (W+D)*t = D + W*s
55//. t = ( W*s + D ) / (W+D)
57{
58public:
59 template <typename Scalar>
60 using Vector2 = std::array<Scalar, 2>;
61
62 template <unsigned int N, typename T>
63 using RegularGrid = MishaK::RegularGrid<N, T>;
64
65private:
66 unsigned int left = 0;
67 unsigned int right = 0;
68 unsigned int bottom = 0;
69 unsigned int top = 0;
70
71public:
72 unsigned int width() const { return left + right; }
73 unsigned int height() const { return bottom + top; }
74
75 template <typename Scalar>
76 static Padding
77 init(unsigned int width, unsigned int height, span<const std::array<Scalar, 2>> texcoords)
78 {
79 Padding padding;
80 Vector2<Scalar> pix_min_corner{Scalar(0.5) / width, Scalar(0.5) / height};
81 Vector2<Scalar> pix_max_corner{Scalar(width - 0.5) / width, Scalar(height - 0.5) / height};
82
83 Vector2<Scalar> tex_min_corner{texcoords[0][0], texcoords[0][1]};
84 Vector2<Scalar> tex_max_corner{texcoords[0][0], texcoords[0][1]};
85 for (size_t i = 0; i < texcoords.size(); i++) {
86 for (int c = 0; c < 2; c++) {
87 tex_min_corner[c] = std::min<Scalar>(tex_min_corner[c], texcoords[i][c]);
88 }
89 for (int c = 0; c < 2; c++) {
90 tex_max_corner[c] = std::max<Scalar>(tex_max_corner[c], texcoords[i][c]);
91 }
92 }
93 logger().debug(
94 "Texture coordinate bounding box : Min ({}, {}). Max ({}, {}). SafeMin ({}, {}). "
95 "SafeMax ({}, {})",
96 tex_min_corner[0],
97 tex_min_corner[1],
98 tex_max_corner[0],
99 tex_max_corner[1],
100 pix_min_corner[0],
101 pix_min_corner[1],
102 pix_max_corner[0],
103 pix_max_corner[1]);
104
105 padding.left = tex_min_corner[0] < pix_min_corner[0]
106 ? (int)ceil((pix_min_corner[0] - tex_min_corner[0]) * width)
107 : 0;
108 padding.bottom = tex_min_corner[1] < pix_min_corner[1]
109 ? (int)ceil((pix_min_corner[1] - tex_min_corner[1]) * height)
110 : 0;
111
112 padding.right = tex_max_corner[0] > pix_max_corner[0]
113 ? (int)ceil((tex_max_corner[0] - pix_max_corner[0]) * width)
114 : 0;
115 padding.top = tex_max_corner[1] > pix_max_corner[1]
116 ? (int)ceil((tex_max_corner[1] - pix_max_corner[1]) * height)
117 : 0;
118
119 // Make image dimensions multiples of 8 (Hardware texture mapping seems to fail if not)
120 {
121 int new_width = width + padding.left + padding.right;
122 int new_height = height + padding.bottom + padding.top;
123
124 int padded_width = 8 * (((new_width - 1) / 8) + 1);
125 int padded_height = 8 * (((new_height - 1) / 8) + 1);
126 padding.left += (padded_width - new_width);
127 padding.bottom += (padded_height - new_height);
128 }
129
130 if (padding.width() || padding.height()) {
131 logger().debug(
132 "Padding applied : Left {}. Right {}. Bottom {}. Top {}.",
133 padding.left,
134 padding.right,
135 padding.bottom,
136 padding.top);
137 } else {
138 logger().debug("No padding required!");
139 }
140
141 return padding;
142 }
143
144 // Add the padding to an image (set new texel values to closest boundary texel)
145 // [WARNING] Assuming the image dimensions match those used to define the object
146 template <typename DataType>
147 void pad(RegularGrid<2, DataType>& im) const
148 {
149 if (!(left || right || bottom || top)) return;
150
151 unsigned int new_width = im.res(0) + left + right;
152 unsigned int new_height = im.res(1) + bottom + top;
153
154 RegularGrid<2, DataType> new_im;
155 new_im.resize(new_width, new_height);
156 for (unsigned int i = 0; i < new_width; i++)
157 for (unsigned int j = 0; j < new_height; j++) {
158 unsigned int ni =
159 std::min<int>(std::max<int>(0, (int)i - (int)left), im.res(0) - 1);
160 unsigned int nj =
161 std::min<int>(std::max<int>(0, (int)j - (int)bottom), im.res(1) - 1);
162 new_im(i, j) = im(ni, nj);
163 }
164 im = new_im;
165 }
166
167 // Remove the padding from an image
168 template <class DataType>
169 void unpad(RegularGrid<2, DataType>& im) const
170 {
171 if (!(left || right || bottom || top)) return;
172
173 unsigned int output_width = im.res(0) - left - right;
174 unsigned int output_height = im.res(1) - bottom - top;
175 RegularGrid<2, DataType> new_im;
176 new_im.resize(output_width, output_height);
177 for (unsigned int i = 0; i < output_width; i++) {
178 for (unsigned int j = 0; j < output_height; j++) {
179 new_im(i, j) = im(left + i, bottom + j);
180 }
181 }
182 im = new_im;
183 }
184
185 template <typename Scalar>
186 void pad(int width, int height, span<std::array<Scalar, 2>> texcoords) const
187 {
188 if (!(left || right || bottom || top)) return;
189
190 int new_width = width + left + right;
191 int new_height = height + bottom + top;
192
193 for (size_t i = 0; i < texcoords.size(); i++) {
194 texcoords[i][0] = (texcoords[i][0] * width + (Scalar)(left)) / new_width;
195 texcoords[i][1] = (texcoords[i][1] * height + (Scalar)(bottom)) / new_height;
196 }
197 }
198
199 template <typename Scalar>
200 void unpad(int width, int height, span<std::array<Scalar, 2>> texcoords) const
201 {
202 if (!(left || right || bottom || top)) return;
203
204 int new_width = width + left + right;
205 int new_height = height + bottom + top;
206
207 for (size_t i = 0; i < texcoords.size(); i++) {
208 texcoords[i][0] = (texcoords[i][0] * new_width - (Scalar)(left)) / width;
209 texcoords[i][1] = (texcoords[i][1] * new_height - (Scalar)(bottom)) / height;
210 }
211 }
212};
213
214} // namespace lagrange::texproc
Definition Padding.h:57
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
::nonstd::span< T, Extent > span
A bounds-safe view for sequences of objects.
Definition span.h:27