1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef MOZILLA_GFX_TOOLS_H_
8 #define MOZILLA_GFX_TOOLS_H_
9
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/MemoryReporting.h" // for MallocSizeOf
12 #include "mozilla/Move.h"
13 #include "mozilla/TypeTraits.h"
14 #include "Types.h"
15 #include "Point.h"
16 #include <math.h>
17
18 namespace mozilla {
19 namespace gfx {
20
IsOperatorBoundByMask(CompositionOp aOp)21 static inline bool IsOperatorBoundByMask(CompositionOp aOp) {
22 switch (aOp) {
23 case CompositionOp::OP_IN:
24 case CompositionOp::OP_OUT:
25 case CompositionOp::OP_DEST_IN:
26 case CompositionOp::OP_DEST_ATOP:
27 case CompositionOp::OP_SOURCE:
28 return false;
29 default:
30 return true;
31 }
32 }
33
34 template <class T>
35 struct ClassStorage {
36 char bytes[sizeof(T)];
37
addrClassStorage38 const T *addr() const { return (const T *)bytes; }
addrClassStorage39 T *addr() { return (T *)(void *)bytes; }
40 };
41
FuzzyEqual(Float aA,Float aB,Float aErr)42 static inline bool FuzzyEqual(Float aA, Float aB, Float aErr) {
43 if ((aA + aErr >= aB) && (aA - aErr <= aB)) {
44 return true;
45 }
46 return false;
47 }
48
NudgeToInteger(float * aVal)49 static inline void NudgeToInteger(float *aVal) {
50 float r = floorf(*aVal + 0.5f);
51 // The error threshold should be proportional to the rounded value. This
52 // bounds the relative error introduced by the nudge operation. However,
53 // when the rounded value is 0, the error threshold can't be proportional
54 // to the rounded value (we'd never round), so we just choose the same
55 // threshold as for a rounded value of 1.
56 if (FuzzyEqual(r, *aVal, r == 0.0f ? 1e-6f : fabs(r * 1e-6f))) {
57 *aVal = r;
58 }
59 }
60
NudgeToInteger(float * aVal,float aErr)61 static inline void NudgeToInteger(float *aVal, float aErr) {
62 float r = floorf(*aVal + 0.5f);
63 if (FuzzyEqual(r, *aVal, aErr)) {
64 *aVal = r;
65 }
66 }
67
NudgeToInteger(double * aVal)68 static inline void NudgeToInteger(double *aVal) {
69 float f = float(*aVal);
70 NudgeToInteger(&f);
71 *aVal = f;
72 }
73
Distance(Point aA,Point aB)74 static inline Float Distance(Point aA, Point aB) {
75 return hypotf(aB.x - aA.x, aB.y - aA.y);
76 }
77
BytesPerPixel(SurfaceFormat aFormat)78 static inline int BytesPerPixel(SurfaceFormat aFormat) {
79 switch (aFormat) {
80 case SurfaceFormat::A8:
81 return 1;
82 case SurfaceFormat::R5G6B5_UINT16:
83 case SurfaceFormat::A16:
84 return 2;
85 case SurfaceFormat::R8G8B8:
86 case SurfaceFormat::B8G8R8:
87 return 3;
88 case SurfaceFormat::HSV:
89 case SurfaceFormat::Lab:
90 return 3 * sizeof(float);
91 case SurfaceFormat::Depth:
92 return sizeof(uint16_t);
93 default:
94 return 4;
95 }
96 }
97
SurfaceFormatForAlphaBitDepth(uint32_t aBitDepth)98 static inline SurfaceFormat SurfaceFormatForAlphaBitDepth(uint32_t aBitDepth) {
99 if (aBitDepth == 8) {
100 return SurfaceFormat::A8;
101 } else if (aBitDepth == 10 || aBitDepth == 12) {
102 return SurfaceFormat::A16;
103 }
104 MOZ_ASSERT_UNREACHABLE("Unsupported alpha bit depth");
105 return SurfaceFormat::UNKNOWN;
106 }
107
IsOpaqueFormat(SurfaceFormat aFormat)108 static inline bool IsOpaqueFormat(SurfaceFormat aFormat) {
109 switch (aFormat) {
110 case SurfaceFormat::B8G8R8X8:
111 case SurfaceFormat::R8G8B8X8:
112 case SurfaceFormat::X8R8G8B8:
113 case SurfaceFormat::YUV:
114 case SurfaceFormat::NV12:
115 case SurfaceFormat::YUV422:
116 case SurfaceFormat::R5G6B5_UINT16:
117 return true;
118 default:
119 return false;
120 }
121 }
122
123 template <typename T, int alignment = 16>
124 struct AlignedArray {
125 typedef T value_type;
126
AlignedArrayAlignedArray127 AlignedArray() : mPtr(nullptr), mStorage(nullptr) {}
128
129 explicit MOZ_ALWAYS_INLINE AlignedArray(size_t aCount, bool aZero = false)
mStorageAlignedArray130 : mStorage(nullptr), mCount(0) {
131 Realloc(aCount, aZero);
132 }
133
~AlignedArrayAlignedArray134 MOZ_ALWAYS_INLINE ~AlignedArray() { Dealloc(); }
135
DeallocAlignedArray136 void Dealloc() {
137 // If we fail this assert we'll need to uncomment the loop below to make
138 // sure dtors are properly invoked. If we do that, we should check that the
139 // comment about compiler dead code elimination is in fact true for all the
140 // compilers that we care about.
141 static_assert(mozilla::IsPod<T>::value,
142 "Destructors must be invoked for this type");
143 #if 0
144 for (size_t i = 0; i < mCount; ++i) {
145 // Since we used the placement |operator new| function to construct the
146 // elements of this array we need to invoke their destructors manually.
147 // For types where the destructor does nothing the compiler's dead code
148 // elimination step should optimize this loop away.
149 mPtr[i].~T();
150 }
151 #endif
152
153 free(mStorage);
154 mStorage = nullptr;
155 mPtr = nullptr;
156 }
157
158 MOZ_ALWAYS_INLINE void Realloc(size_t aCount, bool aZero = false) {
159 free(mStorage);
160 CheckedInt32 storageByteCount =
161 CheckedInt32(sizeof(T)) * aCount + (alignment - 1);
162 if (!storageByteCount.isValid()) {
163 mStorage = nullptr;
164 mPtr = nullptr;
165 mCount = 0;
166 return;
167 }
168 // We don't create an array of T here, since we don't want ctors to be
169 // invoked at the wrong places if we realign below.
170 if (aZero) {
171 // calloc can be more efficient than new[] for large chunks,
172 // so we use calloc/malloc/free for everything.
173 mStorage = static_cast<uint8_t *>(calloc(1, storageByteCount.value()));
174 } else {
175 mStorage = static_cast<uint8_t *>(malloc(storageByteCount.value()));
176 }
177 if (!mStorage) {
178 mStorage = nullptr;
179 mPtr = nullptr;
180 mCount = 0;
181 return;
182 }
183 if (uintptr_t(mStorage) % alignment) {
184 // Our storage does not start at a <alignment>-byte boundary. Make sure
185 // mPtr does!
186 mPtr = (T *)(uintptr_t(mStorage) + alignment -
187 (uintptr_t(mStorage) % alignment));
188 } else {
189 mPtr = (T *)(mStorage);
190 }
191 // Now that mPtr is pointing to the aligned position we can use placement
192 // |operator new| to invoke any ctors at the correct positions. For types
193 // that have a no-op default constructor the compiler's dead code
194 // elimination step should optimize this away.
195 mPtr = new (mPtr) T[aCount];
196 mCount = aCount;
197 }
198
SwapAlignedArray199 void Swap(AlignedArray<T, alignment> &aOther) {
200 mozilla::Swap(mPtr, aOther.mPtr);
201 mozilla::Swap(mStorage, aOther.mStorage);
202 mozilla::Swap(mCount, aOther.mCount);
203 }
204
HeapSizeOfExcludingThisAlignedArray205 size_t HeapSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
206 return aMallocSizeOf(mStorage);
207 }
208
209 MOZ_ALWAYS_INLINE operator T *() { return mPtr; }
210
211 T *mPtr;
212
213 private:
214 uint8_t *mStorage;
215 size_t mCount;
216 };
217
218 /**
219 * Returns aWidth * aBytesPerPixel increased, if necessary, so that it divides
220 * exactly into |alignment|.
221 *
222 * Note that currently |alignment| must be a power-of-2. If for some reason we
223 * want to support NPOT alignment we can revert back to this functions old
224 * implementation.
225 */
226 template <int alignment>
GetAlignedStride(int32_t aWidth,int32_t aBytesPerPixel)227 int32_t GetAlignedStride(int32_t aWidth, int32_t aBytesPerPixel) {
228 static_assert(alignment > 0 && (alignment & (alignment - 1)) == 0,
229 "This implementation currently require power-of-two alignment");
230 const int32_t mask = alignment - 1;
231 CheckedInt32 stride =
232 CheckedInt32(aWidth) * CheckedInt32(aBytesPerPixel) + CheckedInt32(mask);
233 if (stride.isValid()) {
234 return stride.value() & ~mask;
235 }
236 return 0;
237 }
238
239 } // namespace gfx
240 } // namespace mozilla
241
242 #endif /* MOZILLA_GFX_TOOLS_H_ */
243