Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarImage.hpp
blob: aac0105ab8b336610b000c65b178acf549f9fe56 (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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#pragma once

#include "StarString.hpp"
#include "StarVector.hpp"
#include "StarIODevice.hpp"

namespace Star {

enum class PixelFormat : uint8_t {
  RGB24,
  RGBA32,
  BGR24,
  BGRA32,
  RGB_F,
  RGBA_F
};

uint8_t bitsPerPixel(PixelFormat pf);
uint8_t bytesPerPixel(PixelFormat pf);

STAR_EXCEPTION(ImageException, StarException);

STAR_CLASS(Image);

// Holds an image of PixelFormat in row major order, with no padding, with (0,
// 0) defined to be the *lower left* corner.
class Image {
public:
  static Image readPng(IODevicePtr device);
  static bool isPng(IODevicePtr device);
  // Returns the size and pixel format that would be constructed from the given
  // png file, without actually loading it.
  static tuple<Vec2U, PixelFormat> readPngMetadata(IODevicePtr device);

  static Image filled(Vec2U size, Vec4B color, PixelFormat pf = PixelFormat::RGBA32);

  // Creates a zero size image
  Image(PixelFormat pf = PixelFormat::RGBA32);
  Image(Vec2U size, PixelFormat pf = PixelFormat::RGBA32);
  Image(unsigned width, unsigned height, PixelFormat pf = PixelFormat::RGBA32);
  ~Image();

  Image(Image const& image);
  Image(Image&& image);

  Image& operator=(Image const& image);
  Image& operator=(Image&& image);

  uint8_t bitsPerPixel() const;
  uint8_t bytesPerPixel() const;

  unsigned width() const;
  unsigned height() const;
  Vec2U size() const;
  // width or height is 0
  bool empty() const;

  PixelFormat pixelFormat() const;

  // If the image is empty, the data ptr will be null
  uint8_t const* data() const;
  uint8_t* data();

  // Reallocate the image with the given width, height, and pixel format.  The
  // contents of the image are always zeroed after a call to reset.
  void reset(Vec2U size, Maybe<PixelFormat> pf = {});
  void reset(unsigned width, unsigned height, Maybe<PixelFormat> pf = {});

  // Fill the image with a given color
  void fill(Vec3B const& c);
  void fill(Vec4B const& c);

  // Fill a rectangle with a given color
  void fillRect(Vec2U const& pos, Vec2U const& size, Vec3B const& c);
  void fillRect(Vec2U const& pos, Vec2U const& size, Vec4B const& c);

  // Color parameters / return values here are in whatever the internal format
  // is.  Fourth byte, if missing or not provided, is assumed to be 255.  If
  // the position is out of range, then throws an exception.
  void set(Vec2U const& pos, Vec4B const& c);
  void set(Vec2U const& pos, Vec3B const& c);
  Vec4B get(Vec2U const& pos) const;

  // Same as set / get, except color parameters / return values here are always
  // RGB[A], and converts if necessary.
  void setrgb(Vec2U const& pos, Vec4B const& c);
  void setrgb(Vec2U const& pos, Vec3B const& c);
  Vec4B getrgb(Vec2U const& pos) const;

  // Get pixel value, but if pos is out of the normal pixel range, it is
  // clamped back into the valid pixel range.  Returns (0, 0, 0, 0) if image is
  // empty.
  Vec4B clamp(Vec2I const& pos) const;
  Vec4B clamprgb(Vec2I const& pos) const;

  // x / y versions of set / get, for compatibility
  void set(unsigned x, unsigned y, Vec4B const& c);
  void set(unsigned x, unsigned y, Vec3B const& c);
  Vec4B get(unsigned x, unsigned y) const;
  void setrgb(unsigned x, unsigned y, Vec4B const& c);
  void setrgb(unsigned x, unsigned y, Vec3B const& c);
  Vec4B getrgb(unsigned x, unsigned y) const;
  Vec4B clamp(int x, int y) const;
  Vec4B clamprgb(int x, int y) const;

  // Must be 32 bitsPerPixel, no format conversion or bounds checking takes
  // place.  Very fast inline versions.
  void set32(Vec2U const& pos, Vec4B const& c);
  void set32(unsigned x, unsigned y, Vec4B const& c);
  Vec4B get32(unsigned x, unsigned y) const;

  // Must be 24 bitsPerPixel, no format conversion or bounds checking takes
  // place.  Very fast inline versions.
  void set24(Vec2U const& pos, Vec3B const& c);
  void set24(unsigned x, unsigned y, Vec3B const& c);
  Vec3B get24(unsigned x, unsigned y) const;

  // Called as callback(unsigned x, unsigned y, Vec4B const& pixel)
  template <typename CallbackType>
  void forEachPixel(CallbackType&& callback) const;

  // Called as callback(unsigned x, unsigned y, Vec4B& pixel)
  template <typename CallbackType>
  void forEachPixel(CallbackType&& callback);

  // Pixel rectangle, lower left position and size of rectangle.
  Image subImage(Vec2U const& pos, Vec2U const& size) const;

  // Copy given image into this one at pos
  void copyInto(Vec2U const& pos, Image const& image);
  // Draw given image over this one at pos (with alpha composition)
  void drawInto(Vec2U const& pos, Image const& image);

  // Convert this image into the given pixel format
  Image convert(PixelFormat pixelFormat) const;

  void writePng(IODevicePtr device) const;

private:
  uint8_t* m_data;
  unsigned m_width;
  unsigned m_height;
  PixelFormat m_pixelFormat;
};

inline uint8_t bitsPerPixel(PixelFormat pf) {
  switch (pf) {
    case PixelFormat::RGB24:
      return 24;
    case PixelFormat::RGBA32:
      return 32;
    case PixelFormat::BGR24:
      return 24;
    case PixelFormat::BGRA32:
      return 32;
    case PixelFormat::RGB_F:
      return 96;
    default:
      return 128;
  }
}

inline uint8_t bytesPerPixel(PixelFormat pf) {
  switch (pf) {
    case PixelFormat::RGB24:
      return 3;
    case PixelFormat::RGBA32:
      return 4;
    case PixelFormat::BGR24:
      return 3;
    case PixelFormat::BGRA32:
      return 4;
    case PixelFormat::RGB_F:
      return 12;
    default:
      return 16;
  }
}

inline uint8_t Image::bitsPerPixel() const {
  return Star::bitsPerPixel(m_pixelFormat);
}

inline uint8_t Image::bytesPerPixel() const {
  return Star::bytesPerPixel(m_pixelFormat);
}

inline unsigned Image::width() const {
  return m_width;
}

inline unsigned Image::height() const {
  return m_height;
}

inline bool Image::empty() const {
  return m_width == 0 || m_height == 0;
}

inline Vec2U Image::size() const {
  return {m_width, m_height};
}

inline PixelFormat Image::pixelFormat() const {
  return m_pixelFormat;
}

inline const uint8_t* Image::data() const {
  return m_data;
}

inline uint8_t* Image::data() {
  return m_data;
}

inline void Image::set(unsigned x, unsigned y, Vec4B const& c) {
  return set({x, y}, c);
}

inline void Image::set(unsigned x, unsigned y, Vec3B const& c) {
  return set({x, y}, c);
}

inline Vec4B Image::get(unsigned x, unsigned y) const {
  return get({x, y});
}

inline void Image::setrgb(unsigned x, unsigned y, Vec4B const& c) {
  return setrgb({x, y}, c);
}

inline void Image::setrgb(unsigned x, unsigned y, Vec3B const& c) {
  return setrgb({x, y}, c);
}

inline Vec4B Image::getrgb(unsigned x, unsigned y) const {
  return getrgb({x, y});
}

inline Vec4B Image::clamp(int x, int y) const {
  return clamp({x, y});
}

inline Vec4B Image::clamprgb(int x, int y) const {
  return clamprgb({x, y});
}

inline void Image::set32(Vec2U const& pos, Vec4B const& c) {
  set32(pos[0], pos[1], c);
}

inline void Image::set32(unsigned x, unsigned y, Vec4B const& c) {
  starAssert(m_data && x < m_width && y < m_height);
  starAssert(bytesPerPixel() == 4);

  size_t offset = y * m_width * 4 + x * 4;
  m_data[offset] = c[0];
  m_data[offset + 1] = c[1];
  m_data[offset + 2] = c[2];
  m_data[offset + 3] = c[3];
}

inline Vec4B Image::get32(unsigned x, unsigned y) const {
  starAssert(m_data && x < m_width && y < m_height);
  starAssert(bytesPerPixel() == 4);

  Vec4B c;
  size_t offset = y * m_width * 4 + x * 4;
  c[0] = m_data[offset];
  c[1] = m_data[offset + 1];
  c[2] = m_data[offset + 2];
  c[3] = m_data[offset + 3];
  return c;
}

inline void Image::set24(Vec2U const& pos, Vec3B const& c) {
  set24(pos[0], pos[1], c);
}

inline void Image::set24(unsigned x, unsigned y, Vec3B const& c) {
  starAssert(m_data && x < m_width && y < m_height);
  starAssert(bytesPerPixel() == 3);

  size_t offset = y * m_width * 3 + x * 3;
  m_data[offset] = c[0];
  m_data[offset + 1] = c[1];
  m_data[offset + 2] = c[2];
}

inline Vec3B Image::get24(unsigned x, unsigned y) const {
  starAssert(m_data && x < m_width && y < m_height);
  starAssert(bytesPerPixel() == 3);

  Vec3B c;
  size_t offset = y * m_width * 3 + x * 3;
  c[0] = m_data[offset];
  c[1] = m_data[offset + 1];
  c[2] = m_data[offset + 2];
  return c;
}

template <typename CallbackType>
void Image::forEachPixel(CallbackType&& callback) const {
  for (unsigned y = 0; y < m_height; y++) {
    for (unsigned x = 0; x < m_width; x++)
      callback(x, y, get(x, y));
  }
}

template <typename CallbackType>
void Image::forEachPixel(CallbackType&& callback) {
  for (unsigned y = 0; y < m_height; y++) {
    for (unsigned x = 0; x < m_width; x++) {
      Vec4B pixel = get(x, y);
      callback(x, y, pixel);
      set(x, y, pixel);
    }
  }
}

struct ImageView {
  inline bool empty() const { return size.x() == 0 || size.y() == 0; }
  ImageView() = default;
  ImageView(Image const& image);

  Vec2U size{0, 0};
  uint8_t const* data = nullptr;
  PixelFormat format = PixelFormat::RGB24;
};

}