1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <HeifCleanAperture.h>
18
19 namespace android {
20 namespace heif {
21 namespace {
22
23 // |a| and |b| hold int32_t values. The int64_t type is used so that we can negate INT32_MIN without
24 // overflowing int32_t.
calculateGreatestCommonDivisor(int64_t a,int64_t b)25 int64_t calculateGreatestCommonDivisor(int64_t a, int64_t b) {
26 if (a < 0) {
27 a *= -1;
28 }
29 if (b < 0) {
30 b *= -1;
31 }
32 while (b != 0) {
33 int64_t r = a % b;
34 a = b;
35 b = r;
36 }
37 return a;
38 }
39
overflowsInt32(int64_t x)40 bool overflowsInt32(int64_t x) {
41 return (x < INT32_MIN) || (x > INT32_MAX);
42 }
43
calculateCenter(int32_t value)44 Fraction calculateCenter(int32_t value) {
45 Fraction f(value, 2);
46 f.simplify();
47 return f;
48 }
49
50 } // namespace
51
Fraction(int32_t n,int32_t d)52 Fraction::Fraction(int32_t n, int32_t d) {
53 this->n = n;
54 this->d = d;
55 }
56
simplify()57 void Fraction::simplify() {
58 int64_t gcd = calculateGreatestCommonDivisor(n, d);
59 if (gcd > 1) {
60 n = static_cast<int32_t>(n / gcd);
61 d = static_cast<int32_t>(d / gcd);
62 }
63 }
64
commonDenominator(Fraction * f)65 bool Fraction::commonDenominator(Fraction* f) {
66 simplify();
67 f->simplify();
68 if (d == f->d) return true;
69 const int64_t this_d = d;
70 const int64_t fd = f->d;
71 const int64_t thisnNew = n * fd;
72 const int64_t thisdNew = d * fd;
73 const int64_t fnNew = f->n * this_d;
74 const int64_t fdNew = f->d * this_d;
75 if (overflowsInt32(thisnNew) || overflowsInt32(thisdNew) || overflowsInt32(fnNew) ||
76 overflowsInt32(fdNew)) {
77 return false;
78 }
79 n = static_cast<int32_t>(thisnNew);
80 d = static_cast<int32_t>(thisdNew);
81 f->n = static_cast<int32_t>(fnNew);
82 f->d = static_cast<int32_t>(fdNew);
83 return true;
84 }
85
add(Fraction f)86 bool Fraction::add(Fraction f) {
87 if (!commonDenominator(&f)) {
88 return false;
89 }
90
91 const int64_t result = static_cast<int64_t>(n) + f.n;
92 if (overflowsInt32(result)) {
93 return false;
94 }
95 n = static_cast<int32_t>(result);
96 simplify();
97 return true;
98 }
99
subtract(Fraction f)100 bool Fraction::subtract(Fraction f) {
101 if (!commonDenominator(&f)) {
102 return false;
103 }
104
105 const int64_t result = static_cast<int64_t>(n) - f.n;
106 if (overflowsInt32(result)) {
107 return false;
108 }
109 n = static_cast<int32_t>(result);
110 simplify();
111 return true;
112 }
113
convertCleanApertureToRect(uint32_t imageW,uint32_t imageH,const CleanAperture & clap,int32_t * left,int32_t * top,int32_t * right,int32_t * bottom)114 bool convertCleanApertureToRect(uint32_t imageW, uint32_t imageH, const CleanAperture& clap,
115 int32_t* left, int32_t* top, int32_t* right, int32_t* bottom) {
116 // ISO/IEC 14496-12:2020, Section 12.1.4.1:
117 // For horizOff and vertOff, D shall be strictly positive and N may be
118 // positive or negative. For cleanApertureWidth and cleanApertureHeight,
119 // N shall be positive and D shall be strictly positive.
120 if (clap.width.d <= 0 || clap.height.d <= 0 || clap.horizOff.d <= 0 || clap.vertOff.d <= 0 ||
121 clap.width.n < 0 || clap.height.n < 0 || !clap.width.isInteger() ||
122 !clap.height.isInteger() || imageW > INT32_MAX || imageH > INT32_MAX) {
123 return false;
124 }
125
126 const int32_t clapW = clap.width.getInt32();
127 const int32_t clapH = clap.height.getInt32();
128 if (clapW == 0 || clapH == 0) {
129 return false;
130 }
131
132 Fraction centerX = calculateCenter(imageW);
133 Fraction centerY = calculateCenter(imageH);
134 Fraction halfW(clapW, 2);
135 Fraction halfH(clapH, 2);
136
137 if (!centerX.add(clap.horizOff) || !centerX.subtract(halfW) || !centerX.isInteger() ||
138 centerX.n < 0 || !centerY.add(clap.vertOff) || !centerY.subtract(halfH) ||
139 !centerY.isInteger() || centerY.n < 0) {
140 return false;
141 }
142
143 *left = centerX.getInt32();
144 *top = centerY.getInt32();
145 *right = *left + clapW;
146 *bottom = *top + clapH;
147
148 // Make sure that the crop rect is within the image bounds.
149 if (*left > (UINT32_MAX - clapW) || *right > imageW || *top > (UINT32_MAX - clapH) ||
150 *bottom > imageH) {
151 return false;
152 }
153 return true;
154 }
155
156 } // namespace heif
157 } // namespace android
158