summaryrefslogtreecommitdiff
path: root/gfx/2d/Tools.h
blob: 585cbbb2c2b3316decbeb65953f89dbb80e65992 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef MOZILLA_GFX_TOOLS_H_
#define MOZILLA_GFX_TOOLS_H_

#include "mozilla/CheckedInt.h"
#include "mozilla/Move.h"
#include "mozilla/TypeTraits.h"
#include "Types.h"
#include "Point.h"
#include <math.h>

namespace mozilla {
namespace gfx {

static inline bool
IsOperatorBoundByMask(CompositionOp aOp) {
  switch (aOp) {
  case CompositionOp::OP_IN:
  case CompositionOp::OP_OUT:
  case CompositionOp::OP_DEST_IN:
  case CompositionOp::OP_DEST_ATOP:
  case CompositionOp::OP_SOURCE:
    return false;
  default:
    return true;
  }
}

template <class T>
struct ClassStorage
{
  char bytes[sizeof(T)];

  const T *addr() const { return (const T *)bytes; }
  T *addr() { return (T *)(void *)bytes; }
};

static inline bool
FuzzyEqual(Float aA, Float aB, Float aErr)
{
  if ((aA + aErr >= aB) && (aA - aErr <= aB)) {
    return true;
  }
  return false;
}

static inline void
NudgeToInteger(float *aVal)
{
  float r = floorf(*aVal + 0.5f);
  // The error threshold should be proportional to the rounded value. This
  // bounds the relative error introduced by the nudge operation. However,
  // when the rounded value is 0, the error threshold can't be proportional
  // to the rounded value (we'd never round), so we just choose the same
  // threshold as for a rounded value of 1.
  if (FuzzyEqual(r, *aVal, r == 0.0f ? 1e-6f : fabs(r*1e-6f))) {
    *aVal = r;
  }
}

static inline void
NudgeToInteger(float *aVal, float aErr)
{
  float r = floorf(*aVal + 0.5f);
  if (FuzzyEqual(r, *aVal, aErr)) {
    *aVal = r;
  }
}

static inline Float
Distance(Point aA, Point aB)
{
  return hypotf(aB.x - aA.x, aB.y - aA.y);
}

static inline int
BytesPerPixel(SurfaceFormat aFormat)
{
  switch (aFormat) {
  case SurfaceFormat::A8:
    return 1;
  case SurfaceFormat::R5G6B5_UINT16:
    return 2;
  case SurfaceFormat::R8G8B8:
  case SurfaceFormat::B8G8R8:
    return 3;
  case SurfaceFormat::HSV:
  case SurfaceFormat::Lab:
    return 3 * sizeof(float);
  case SurfaceFormat::Depth:
    return sizeof(uint16_t);
  default:
    return 4;
  }
}

static inline bool
IsOpaqueFormat(SurfaceFormat aFormat) {
  switch (aFormat) {
    case SurfaceFormat::B8G8R8X8:
    case SurfaceFormat::R8G8B8X8:
    case SurfaceFormat::X8R8G8B8:
    case SurfaceFormat::YUV:
    case SurfaceFormat::NV12:
    case SurfaceFormat::YUV422:
    case SurfaceFormat::R5G6B5_UINT16:
      return true;
    default:
      return false;
  }
}

template<typename T, int alignment = 16>
struct AlignedArray
{
  typedef T value_type;

  AlignedArray()
    : mPtr(nullptr)
    , mStorage(nullptr)
  {
  }

  explicit MOZ_ALWAYS_INLINE AlignedArray(size_t aCount, bool aZero = false)
    : mStorage(nullptr)
    , mCount(0)
  {
    Realloc(aCount, aZero);
  }

  MOZ_ALWAYS_INLINE ~AlignedArray()
  {
    Dealloc();
  }

  void Dealloc()
  {
    // If we fail this assert we'll need to uncomment the loop below to make
    // sure dtors are properly invoked. If we do that, we should check that the
    // comment about compiler dead code elimination is in fact true for all the
    // compilers that we care about.
    static_assert(mozilla::IsPod<T>::value,
                  "Destructors must be invoked for this type");
#if 0
    for (size_t i = 0; i < mCount; ++i) {
      // Since we used the placement |operator new| function to construct the
      // elements of this array we need to invoke their destructors manually.
      // For types where the destructor does nothing the compiler's dead code
      // elimination step should optimize this loop away.
      mPtr[i].~T();
    }
#endif

    free(mStorage);
    mStorage = nullptr;
    mPtr = nullptr;
  }

  MOZ_ALWAYS_INLINE void Realloc(size_t aCount, bool aZero = false)
  {
    free(mStorage);
    CheckedInt32 storageByteCount =
      CheckedInt32(sizeof(T)) * aCount + (alignment - 1);
    if (!storageByteCount.isValid()) {
      mStorage = nullptr;
      mPtr = nullptr;
      mCount = 0;
      return;
    }
    // We don't create an array of T here, since we don't want ctors to be
    // invoked at the wrong places if we realign below.
    if (aZero) {
      // calloc can be more efficient than new[] for large chunks,
      // so we use calloc/malloc/free for everything.
      mStorage = static_cast<uint8_t *>(calloc(1, storageByteCount.value()));
    } else {
      mStorage = static_cast<uint8_t *>(malloc(storageByteCount.value()));
    }
    if (!mStorage) {
      mStorage = nullptr;
      mPtr = nullptr;
      mCount = 0;
      return;
    }
    if (uintptr_t(mStorage) % alignment) {
      // Our storage does not start at a <alignment>-byte boundary. Make sure mPtr does!
      mPtr = (T*)(uintptr_t(mStorage) + alignment - (uintptr_t(mStorage) % alignment));
    } else {
      mPtr = (T*)(mStorage);
    }
    // Now that mPtr is pointing to the aligned position we can use placement
    // |operator new| to invoke any ctors at the correct positions. For types
    // that have a no-op default constructor the compiler's dead code
    // elimination step should optimize this away.
    mPtr = new (mPtr) T[aCount];
    mCount = aCount;
  }

  void Swap(AlignedArray<T, alignment>& aOther)
  {
    mozilla::Swap(mPtr, aOther.mPtr);
    mozilla::Swap(mStorage, aOther.mStorage);
    mozilla::Swap(mCount, aOther.mCount);
  }

  MOZ_ALWAYS_INLINE operator T*()
  {
    return mPtr;
  }

  T *mPtr;

private:
  uint8_t *mStorage;
  size_t mCount;
};

/**
 * Returns aWidth * aBytesPerPixel increased, if necessary, so that it divides
 * exactly into |alignment|.
 *
 * Note that currently |alignment| must be a power-of-2. If for some reason we
 * want to support NPOT alignment we can revert back to this functions old
 * implementation.
 */
template<int alignment>
int32_t GetAlignedStride(int32_t aWidth, int32_t aBytesPerPixel)
{
  static_assert(alignment > 0 && (alignment & (alignment-1)) == 0,
                "This implementation currently require power-of-two alignment");
  const int32_t mask = alignment - 1;
  CheckedInt32 stride = CheckedInt32(aWidth) * CheckedInt32(aBytesPerPixel) + CheckedInt32(mask);
  if (stride.isValid()) {
    return stride.value() & ~mask;
  }
  return 0;
}

} // namespace gfx
} // namespace mozilla

#endif /* MOZILLA_GFX_TOOLS_H_ */