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