ObjFW
OFImage+Private.h
1 /*
2  * Copyright (c) 2008-2026 Jonathan Schleifer <js@nil.im>
3  *
4  * All rights reserved.
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License version 3.0 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13  * version 3.0 for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * version 3.0 along with this program. If not, see
17  * <https://www.gnu.org/licenses/>.
18  */
19 
20 #import "OFImage.h"
21 #import "OFColorSpace.h"
22 
23 OF_ASSUME_NONNULL_BEGIN
24 
25 static OF_INLINE void
26 _OFReadRGB888Pixel(const uint8_t *pixels, size_t x, size_t y, size_t width,
27  uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *alpha)
28 {
29  pixels += (x + y * width) * 3;
30 
31  *red = pixels[0];
32  *green = pixels[1];
33  *blue = pixels[2];
34  *alpha = 255;
35 }
36 
37 static OF_INLINE void
38 _OFReadBGR888Pixel(const uint8_t *pixels, size_t x, size_t y, size_t width,
39  uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *alpha)
40 {
41  pixels += (x + y * width) * 3;
42 
43  *red = pixels[2];
44  *green = pixels[1];
45  *blue = pixels[0];
46  *alpha = 255;
47 }
48 
49 static OF_INLINE void
50 _OFReadRGBA8888Pixel(const uint32_t *pixels, size_t x, size_t y, size_t width,
51  uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *alpha)
52 {
53  uint32_t value = pixels[x + y * width];
54 
55  *red = (value & 0xFF000000) >> 24;
56  *green = (value & 0x00FF0000) >> 16;
57  *blue = (value & 0x0000FF00) >> 8;
58  *alpha = (value & 0x000000FF);
59 }
60 
61 static OF_INLINE void
62 _OFReadARGB8888Pixel(const uint32_t *pixels, size_t x, size_t y, size_t width,
63  uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *alpha)
64 {
65  uint32_t value = pixels[x + y * width];
66 
67  *red = (value & 0x00FF0000) >> 16;
68  *green = (value & 0x0000FF00) >> 8;
69  *blue = (value & 0x000000FF);
70  *alpha = (value & 0xFF000000) >> 24;
71 }
72 
73 static OF_INLINE void
74 _OFReadABGR8888Pixel(const uint32_t *pixels, size_t x, size_t y, size_t width,
75  uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *alpha)
76 {
77  uint32_t value = pixels[x + y * width];
78 
79  *red = (value & 0x000000FF);
80  *green = (value & 0x0000FF00) >> 8;
81  *blue = (value & 0x00FF0000) >> 16;
82  *alpha = (value & 0xFF000000) >> 24;
83 }
84 
85 static OF_INLINE void
86 _OFReadBGRA8888Pixel(const uint32_t *pixels, size_t x, size_t y, size_t width,
87  uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *alpha)
88 {
89  uint32_t value = pixels[x + y * width];
90 
91  *red = (value & 0x0000FF00) >> 8;
92  *green = (value & 0x00FF0000) >> 16;
93  *blue = (value & 0xFF000000) >> 24;
94  *alpha = (value & 0x000000FF);
95 }
96 
97 static OF_INLINE bool
98 _OFReadPixelInt8(const void *pixels, OFPixelFormat format, size_t x, size_t y,
99  size_t width, uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *alpha)
100 {
101  switch (format) {
102  case OFPixelFormatRGB888:
103  _OFReadRGB888Pixel(pixels, x, y, width, red, green, blue,
104  alpha);
105  return true;
106  case OFPixelFormatBGR888:
107  _OFReadBGR888Pixel(pixels, x, y, width, red, green, blue,
108  alpha);
109  return true;
110  case OFPixelFormatRGBA8888:
111  _OFReadRGBA8888Pixel(pixels, x, y, width, red, green, blue,
112  alpha);
113  return true;
114  case OFPixelFormatARGB8888:
115  _OFReadARGB8888Pixel(pixels, x, y, width, red, green, blue,
116  alpha);
117  return true;
118  case OFPixelFormatABGR8888:
119  _OFReadABGR8888Pixel(pixels, x, y, width, red, green, blue,
120  alpha);
121  return true;
122  case OFPixelFormatBGRA8888:
123  _OFReadBGRA8888Pixel(pixels, x, y, width, red, green, blue,
124  alpha);
125  return true;
126  default:
127  return false;
128  }
129 }
130 
131 static OF_INLINE void
132 _OFReadRGB565Pixel(const uint16_t *pixels, size_t x, size_t y, size_t width,
133  float *red, float *green, float *blue, float *alpha)
134 {
135  uint16_t value = pixels[x + y * width];
136 
137  *red = ((value & 0xF800) >> 11) / 31.0f;
138  *green = ((value & 0x07E0) >> 5) / 63.0f;
139  *blue = (value & 0x001F) / 31.0f;
140  *alpha = 1.0f;
141 }
142 
143 static OF_INLINE void
144 _OFReadRGBA16161616FPPixel(const OFFloat16 *pixels, size_t x, size_t y,
145  size_t width, float *red, float *green, float *blue, float *alpha)
146 {
147  *red = OFFloat16ToFloat(pixels[(x + y * width) * 4]);
148  *green = OFFloat16ToFloat(pixels[(x + y * width) * 4 + 1]);
149  *blue = OFFloat16ToFloat(pixels[(x + y * width) * 4 + 2]);
150  *alpha = OFFloat16ToFloat(pixels[(x + y * width) * 4 + 3]);
151 }
152 
153 static OF_INLINE void
154 _OFReadRGBA32323232FPPixel(const float *pixels, size_t x, size_t y,
155  size_t width, float *red, float *green, float *blue, float *alpha)
156 {
157  *red = pixels[(x + y * width) * 4];
158  *green = pixels[(x + y * width) * 4 + 1];
159  *blue = pixels[(x + y * width) * 4 + 2];
160  *alpha = pixels[(x + y * width) * 4 + 3];
161 }
162 
163 static OF_INLINE bool
164 _OFReadPixel(const void *pixels, OFPixelFormat format, size_t x, size_t y,
165  size_t width, float *red, float *green, float *blue, float *alpha)
166 {
167  uint8_t redInt8 = 0, greenInt8 = 0, blueInt8 = 0, alphaInt8 = 0;
168 
169  switch (format) {
170  case OFPixelFormatRGB565:
171  _OFReadRGB565Pixel(pixels, x, y, width, red, green, blue,
172  alpha);
173  return true;
174  case OFPixelFormatRGBA16161616FP:
175  _OFReadRGBA16161616FPPixel(pixels, x, y, width, red, green,
176  blue, alpha);
177  return true;
178  case OFPixelFormatRGBA32323232FP:
179  _OFReadRGBA32323232FPPixel(pixels, x, y, width, red, green,
180  blue, alpha);
181  return true;
182  default:
183  break;
184  }
185 
186  if OF_UNLIKELY (!_OFReadPixelInt8(pixels, format, x, y, width,
187  &redInt8, &greenInt8, &blueInt8, &alphaInt8))
188  return false;
189 
190  *red = redInt8 / 255.0f;
191  *green = greenInt8 / 255.0f;
192  *blue = blueInt8 / 255.0f;
193  *alpha = alphaInt8 / 255.0f;
194 
195  return true;
196 }
197 
198 static OF_INLINE bool
199 _OFReadAveragedPixel(const void *pixels, OFPixelFormat format, float x, float y,
200  size_t width, size_t clampX, size_t clampY,
202  float *red, float *green, float *blue, float *alpha)
203 {
204  size_t xInt = x, yInt = y, nextXInt = xInt + 1, nextYInt = yInt + 1;
205  OF_ALIGN(16) OFVector4D vectors[4], averaged;
206  float scales[4];
207 
208  if (x == xInt && y == yInt)
209  return _OFReadPixel(pixels, format, xInt, yInt, width,
210  red, green, blue, alpha);
211 
212  if (nextXInt >= clampX || x == xInt)
213  nextXInt = xInt;
214  if (nextYInt >= clampY || y == yInt)
215  nextYInt = yInt;
216 
217  if (!_OFReadPixel(pixels, format, xInt, yInt, width,
218  &vectors[0].x, &vectors[0].y, &vectors[0].z, &vectors[0].w))
219  return false;
220 
221  if (!_OFReadPixel(pixels, format, nextXInt, yInt, width,
222  &vectors[1].x, &vectors[1].y, &vectors[1].z, &vectors[1].w))
223  return false;
224 
225  if (!_OFReadPixel(pixels, format, xInt, nextYInt, width,
226  &vectors[2].x, &vectors[2].y, &vectors[2].z, &vectors[2].w))
227  return false;
228 
229  if (!_OFReadPixel(pixels, format, nextXInt, nextYInt, width,
230  &vectors[3].x, &vectors[3].y, &vectors[3].z, &vectors[3].w))
231  return false;
232 
233  scales[0] = (1.0f - (x - xInt)) * (1.0f - (y - yInt));
234  scales[1] = (x - xInt) * (1.0f - (y - yInt));
235  scales[2] = (1.0f - (x - xInt)) * (y - yInt);
236  scales[3] = (x - xInt) * (y - yInt);
237 
238  if (EOTF != NULL)
239  EOTF(vectors, 4);
240 
241  averaged = OFMakeVector4D(0.0f, 0.0f, 0.0f, 0.0f);
242  for (uint_fast8_t i = 0; i < 4; i++)
243  averaged = OFAddVectors4D(averaged,
244  OFMultiplyVector4D(vectors[i], scales[i]));
245 
246  if (OETF != NULL)
247  OETF(&averaged, 1);
248 
249  *red = averaged.x;
250  *green = averaged.y;
251  *blue = averaged.z;
252  *alpha = averaged.w;
253 
254  return true;
255 }
256 
257 static OF_INLINE void
258 _OFWriteRGB888Pixel(uint8_t *pixels, size_t x, size_t y, size_t width,
259  uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
260 {
261  pixels += (x + y * width) * 3;
262 
263  pixels[0] = red;
264  pixels[1] = green;
265  pixels[2] = blue;
266 }
267 
268 static OF_INLINE void
269 _OFWriteBGR888Pixel(uint8_t *pixels, size_t x, size_t y, size_t width,
270  uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
271 {
272  pixels += (x + y * width) * 3;
273 
274  pixels[0] = blue;
275  pixels[1] = green;
276  pixels[2] = red;
277 }
278 
279 static OF_INLINE void
280 _OFWriteRGBA8888Pixel(uint32_t *pixels, size_t x, size_t y, size_t width,
281  uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
282 {
283  pixels[x + y * width] = red << 24 | green << 16 | blue << 8 | alpha;
284 }
285 
286 static OF_INLINE void
287 _OFWriteARGB8888Pixel(uint32_t *pixels, size_t x, size_t y, size_t width,
288  uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
289 {
290  pixels[x + y * width] = alpha << 24 | red << 16 | green << 8 | blue;
291 }
292 
293 static OF_INLINE void
294 _OFWriteABGR8888Pixel(uint32_t *pixels, size_t x, size_t y, size_t width,
295  uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
296 {
297  pixels[x + y * width] = alpha << 24 | blue << 16 | green << 8 | red;
298 }
299 
300 static OF_INLINE void
301 _OFWriteBGRA8888Pixel(uint32_t *pixels, size_t x, size_t y, size_t width,
302  uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
303 {
304  pixels[x + y * width] = blue << 24 | green << 16 | red << 8 | alpha;
305 }
306 
307 static OF_INLINE bool
308 _OFWritePixelInt8(void *pixels, OFPixelFormat format, size_t x, size_t y,
309  size_t width, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
310 {
311  switch (format) {
312  case OFPixelFormatRGB888:
313  _OFWriteRGB888Pixel(pixels, x, y, width, red, green, blue,
314  alpha);
315  return true;
316  case OFPixelFormatRGBA8888:
317  _OFWriteRGBA8888Pixel(pixels, x, y, width, red, green, blue,
318  alpha);
319  return true;
320  case OFPixelFormatARGB8888:
321  _OFWriteARGB8888Pixel(pixels, x, y, width, red, green, blue,
322  alpha);
323  return true;
324  case OFPixelFormatBGR888:
325  _OFWriteBGR888Pixel(pixels, x, y, width, red, green, blue,
326  alpha);
327  return true;
328  case OFPixelFormatABGR8888:
329  _OFWriteABGR8888Pixel(pixels, x, y, width, red, green, blue,
330  alpha);
331  return true;
332  case OFPixelFormatBGRA8888:
333  _OFWriteBGRA8888Pixel(pixels, x, y, width, red, green, blue,
334  alpha);
335  return true;
336  default:
337  return false;
338  }
339 }
340 
341 static OF_INLINE void
342 _OFWriteRGB565Pixel(uint16_t *pixels, size_t x, size_t y, size_t width,
343  float red, float green, float blue, float alpha)
344 {
345  uint8_t redInt, greenInt, blueInt;
346 
347  if OF_UNLIKELY (red > 1.0f)
348  red = 1.0f;
349  else if OF_UNLIKELY (red < 0.0f)
350  red = 0.0f;
351  if OF_UNLIKELY (green > 1.0f)
352  green = 1.0f;
353  else if OF_UNLIKELY (green < 0.0f)
354  green = 0.0f;
355  if OF_UNLIKELY (blue > 1.0f)
356  blue = 1.0f;
357  else if OF_UNLIKELY (blue < 0.0f)
358  blue = 0.0f;
359  if OF_UNLIKELY (alpha > 1.0f)
360  alpha = 1.0f;
361 
362  redInt = roundf(red * 31.0f);
363  greenInt = roundf(green * 63.0f);
364  blueInt = roundf(blue * 31.0f);
365 
366  pixels[x + y * width] = redInt << 11 | greenInt << 5 | blueInt;
367 }
368 
369 static OF_INLINE void
370 _OFWriteRGBA16161616FPPixel(OFFloat16 *pixels, size_t x, size_t y, size_t width,
371  float red, float green, float blue, float alpha)
372 {
373  pixels[(x + y * width) * 4] = OFFloat16FromFloat(red);
374  pixels[(x + y * width) * 4 + 1] = OFFloat16FromFloat(green);
375  pixels[(x + y * width) * 4 + 2] = OFFloat16FromFloat(blue);
376  pixels[(x + y * width) * 4 + 3] = OFFloat16FromFloat(alpha);
377 }
378 
379 static OF_INLINE void
380 _OFWriteRGBA32323232FPPixel(float *pixels, size_t x, size_t y, size_t width,
381  float red, float green, float blue, float alpha)
382 {
383  pixels[(x + y * width) * 4] = red;
384  pixels[(x + y * width) * 4 + 1] = green;
385  pixels[(x + y * width) * 4 + 2] = blue;
386  pixels[(x + y * width) * 4 + 3] = alpha;
387 }
388 
389 static OF_INLINE bool
390 _OFWritePixel(void *pixels, OFPixelFormat format, size_t x, size_t y,
391  size_t width, float red, float green, float blue, float alpha)
392 {
393  switch (format) {
394  case OFPixelFormatRGB565:
395  _OFWriteRGB565Pixel(pixels, x, y, width, red, green, blue,
396  alpha);
397  return true;
398  case OFPixelFormatRGBA16161616FP:
399  _OFWriteRGBA16161616FPPixel(pixels, x, y, width, red, green,
400  blue, alpha);
401  return true;
402  case OFPixelFormatRGBA32323232FP:
403  _OFWriteRGBA32323232FPPixel(pixels, x, y, width, red, green,
404  blue, alpha);
405  return true;
406  default:
407  break;
408  }
409 
410  if OF_UNLIKELY (red > 1.0f)
411  red = 1.0f;
412  else if OF_UNLIKELY (red < 0.0f)
413  red = 0.0f;
414  if OF_UNLIKELY (green > 1.0f)
415  green = 1.0f;
416  else if OF_UNLIKELY (green < 0.0f)
417  green = 0.0f;
418  if OF_UNLIKELY (blue > 1.0f)
419  blue = 1.0f;
420  else if OF_UNLIKELY (blue < 0.0f)
421  blue = 0.0f;
422  if OF_UNLIKELY (alpha > 1.0f)
423  alpha = 1.0f;
424  else if OF_UNLIKELY (alpha < 0.0f)
425  alpha = 0.0f;
426 
427  return _OFWritePixelInt8(pixels, format, x, y, width,
428  roundf(red * 255.0f), roundf(green * 255.0f), roundf(blue * 255.0f),
429  roundf(alpha * 255.0f));
430 }
431 
432 OF_DIRECT_MEMBERS
433 @interface OFImage ()
434 - (instancetype)of_init OF_METHOD_FAMILY(init);
435 @end
436 
437 OF_ASSUME_NONNULL_END
static OF_INLINE OFVector4D OFMultiplyVector4D(OFVector4D vector, float scalar)
Multiplies the specified vector with a scalar.
Definition: OFObject.h:640
static OF_INLINE OFVector4D OFAddVectors4D(OFVector4D vector1, OFVector4D vector2)
Adds the two specified vectors.
Definition: OFObject.h:612
static OF_INLINE OFVector4D OF_CONST_FUNC OFMakeVector4D(float x, float y, float z, float w)
Creates a new OFVector4D.
Definition: OFObject.h:572
instancetype init()
Initializes an already allocated object.
Definition: OFObject.m:671
__extension__ typedef _Float16 OFFloat16
A type for 16 bit floating point numbers.
Definition: macros.h:961
void(* OFColorSpaceTransferFunction)(OFVector4D *vectors, size_t count)
A transfer function for a color space.
Definition: OFColorSpace.h:42
A vector in 4D space.
Definition: OFObject.h:551
A class representing an image.
Definition: OFImage.h:115