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 std::swap(tex_min_corner[1], tex_max_corner[1]);
94 tex_min_corner[1] = 1.0 - tex_min_corner[1];
95 tex_max_corner[1] = 1.0 - tex_max_corner[1];
96 logger().debug(
97 "Texture coordinate bounding box : Min ({}, {}). Max ({}, {}). SafeMin ({}, {}). "
98 "SafeMax ({}, {})",
99 tex_min_corner[0],
100 tex_min_corner[1],
101 tex_max_corner[0],
102 tex_max_corner[1],
103 pix_min_corner[0],
104 pix_min_corner[1],
105 pix_max_corner[0],
106 pix_max_corner[1]);
107
108 padding.left = tex_min_corner[0] < pix_min_corner[0]
109 ? (int)ceil((pix_min_corner[0] - tex_min_corner[0]) * width)
110 : 0;
111 padding.bottom = tex_min_corner[1] < pix_min_corner[1]
112 ? (int)ceil((pix_min_corner[1] - tex_min_corner[1]) * height)
113 : 0;
114
115 padding.right = tex_max_corner[0] > pix_max_corner[0]
116 ? (int)ceil((tex_max_corner[0] - pix_max_corner[0]) * width)
117 : 0;
118 padding.top = tex_max_corner[1] > pix_max_corner[1]
119 ? (int)ceil((tex_max_corner[1] - pix_max_corner[1]) * height)
120 : 0;
121
122 // Make image dimensions multiples of 8 (Hardware texture mapping seems to fail if not)
123 {
124 int new_width = width + padding.left + padding.right;
125 int new_height = height + padding.bottom + padding.top;
126
127 int padded_width = 8 * (((new_width - 1) / 8) + 1);
128 int padded_height = 8 * (((new_height - 1) / 8) + 1);
129 padding.left += (padded_width - new_width);
130 padding.bottom += (padded_height - new_height);
131 }
132
133 if (padding.width() || padding.height()) {
134 logger().debug(
135 "Padding applied : Left {}. Right {}. Bottom {}. Top {}.",
136 padding.left,
137 padding.right,
138 padding.bottom,
139 padding.top);
140 } else {
141 logger().debug("No padding required!");
142 }
143
144 return padding;
145 }
146
147 // Add the padding to an image (set new texel values to closest boundary texel)
148 // [WARNING] Assuming the image dimensions match those used to define the object
149 template <typename DataType>
150 void pad(RegularGrid<2, DataType>& im) const
151 {
152 if (!(left || right || bottom || top)) return;
153
154 unsigned int new_width = im.res(0) + left + right;
155 unsigned int new_height = im.res(1) + bottom + top;
156
157 RegularGrid<2, DataType> new_im;
158 new_im.resize(new_width, new_height);
159 for (unsigned int i = 0; i < new_width; i++)
160 for (unsigned int j = 0; j < new_height; j++) {
161 unsigned int ni =
162 std::min<int>(std::max<int>(0, (int)i - (int)left), im.res(0) - 1);
163 unsigned int nj =
164 std::min<int>(std::max<int>(0, (int)j - (int)bottom), im.res(1) - 1);
165 new_im(i, j) = im(ni, nj);
166 }
167 im = new_im;
168 }
169
170 // Remove the padding from an image
171 template <class DataType>
172 void unpad(RegularGrid<2, DataType>& im) const
173 {
174 if (!(left || right || bottom || top)) return;
175
176 unsigned int output_width = im.res(0) - left - right;
177 unsigned int output_height = im.res(1) - bottom - top;
178 RegularGrid<2, DataType> new_im;
179 new_im.resize(output_width, output_height);
180 for (unsigned int i = 0; i < output_width; i++) {
181 for (unsigned int j = 0; j < output_height; j++) {
182 new_im(i, j) = im(left + i, bottom + j);
183 }
184 }
185 im = new_im;
186 }
187
188 template <typename Scalar>
189 void pad(int width, int height, span<std::array<Scalar, 2>> texcoords) const
190 {
191 if (!(left || right || bottom || top)) return;
192
193 int new_width = width + left + right;
194 int new_height = height + bottom + top;
195
196 for (size_t i = 0; i < texcoords.size(); i++) {
197 texcoords[i][0] = (texcoords[i][0] * width + (Scalar)(left)) / new_width;
198 texcoords[i][1] =
199 1.0 - ((1.0 - texcoords[i][1]) * height + (Scalar)(bottom)) / new_height;
200 }
201 }
202};
203
204} // 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