Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OOPixMapChannelOperations.m
Go to the documentation of this file.
1/*
2
3OOPixMapChannelOperations.m
4
5
6Copyright (C) 2010-2013 Jens Ayton
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in all
16copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25
26*/
27
29#import "OOCPUInfo.h"
30
31
32static void ExtractChannel_4(OOPixMap *ioPixMap, uint8_t channelIndex);
33static void ToRGBA_1(OOPixMap srcPx, OOPixMap dstPx);
34static void ToRGBA_2(OOPixMap srcPx, OOPixMap dstPx);
35static void ModulateUniform_4(OOPixMap pixMap, uint16_t f0, uint16_t f1, uint16_t f2, uint16_t f3);
36static void ModulatePixMap_4(OOPixMap mainPx, OOPixMap otherPx);
37static void AddPixMap_4(OOPixMap mainPx, OOPixMap otherPx);
38
39
40BOOL OOExtractPixMapChannel(OOPixMap *ioPixMap, uint8_t channelIndex, BOOL compactWhenDone)
41{
42 if (EXPECT_NOT(ioPixMap == NULL || !OOIsValidPixMap(*ioPixMap) || ioPixMap->format != kOOPixMapRGBA || channelIndex > 3))
43 {
44 return NO;
45 }
46
47 ExtractChannel_4(ioPixMap, channelIndex);
48
49 ioPixMap->format = kOOPixMapGrayscale;
50 ioPixMap->rowBytes = ioPixMap->width;
51
52 if (compactWhenDone)
53 {
54 OOCompactPixMap(ioPixMap);
55 }
56
57 return YES;
58}
59
60
61static void ExtractChannel_4(OOPixMap *ioPixMap, uint8_t channelIndex)
62{
63 NSCParameterAssert(ioPixMap != NULL);
64
65 uint32_t *src;
66 uint8_t *dst;
67 uint_fast8_t shift;
68 uint_fast32_t xCount, y;
69
70 dst = ioPixMap->pixels;
71 shift = 8 * channelIndex;
72
73 for (y = 0; y < ioPixMap->height; y++)
74 {
75 src = (uint32_t *)((char *)ioPixMap->pixels + y * ioPixMap->rowBytes);
76 xCount = ioPixMap->width;
77
78 do
79 {
80 *dst++ = (*src++ >> shift) & 0xFF;
81 }
82 while (--xCount);
83 }
84}
85
86
87BOOL OOPixMapToRGBA(OOPixMap *ioPixMap)
88{
89 if (EXPECT_NOT(ioPixMap == NULL || !OOIsValidPixMap(*ioPixMap))) return NO;
90 if (ioPixMap->format == kOOPixMapRGBA) return YES;
91
92 OOPixMap temp = OOAllocatePixMap(ioPixMap->width, ioPixMap->height, 4, 0, 0);
93 if (EXPECT_NOT(OOIsNullPixMap(temp))) return NO;
94
95 BOOL OK = NO;
96 switch (ioPixMap->format)
97 {
99 ToRGBA_1(*ioPixMap, temp);
100 OK = YES;
101 break;
102
104 ToRGBA_2(*ioPixMap, temp);
105 OK = YES;
106 break;
107
108 case kOOPixMapRGBA:
110 OK = NO;
111 break;
112 // No default, because -Wswitch-enum is our friend.
113 }
114
115 if (OK)
116 {
117 free(ioPixMap->pixels);
118 *ioPixMap = temp;
119 }
120 else
121 {
122 free(temp.pixels);
123 }
124
125 return OK;
126}
127
128
129static void ToRGBA_1(OOPixMap srcPx, OOPixMap dstPx)
130{
131 NSCParameterAssert(OOPixMapBytesPerPixel(srcPx) == 1 && dstPx.format == kOOPixMapRGBA && srcPx.width == dstPx.width && srcPx.height == dstPx.height);
132
133 uint8_t *src;
134 uint32_t *dst;
135 uint_fast32_t xCount, y;
136
137 dst = dstPx.pixels;
138
139 for (y = 0; y < srcPx.height; y++)
140 {
141 src = (uint8_t *)((char *)srcPx.pixels + y * srcPx.rowBytes);
142 xCount = srcPx.width;
143
144 do
145 {
146 *dst++ = (*src++ * 0x00010101) | 0xFF000000;
147 }
148 while (--xCount);
149 }
150}
151
152
153static void ToRGBA_2(OOPixMap srcPx, OOPixMap dstPx)
154{
155 NSCParameterAssert(OOPixMapBytesPerPixel(srcPx) == 2 && dstPx.format == kOOPixMapRGBA && srcPx.width == dstPx.width && srcPx.height == dstPx.height);
156
157 uint16_t *src;
158 uint_fast32_t px;
159 uint32_t *dst;
160 uint_fast32_t xCount, y;
161
162 dst = dstPx.pixels;
163
164 for (y = 0; y < srcPx.height; y++)
165 {
166 src = (uint16_t *)((char *)srcPx.pixels + y * srcPx.rowBytes);
167 xCount = srcPx.width;
168
169 do
170 {
171 px = *src++;
172#if OOLITE_BIG_ENDIAN
173 *dst++ = (((px & 0xFF00) >> 8) * 0x00010101) | ((px & 0x00FF) << 24);
174#elif OOLITE_LITTLE_ENDIAN
175 *dst++ = ((px & 0x00FF) * 0x00010101) | ((px & 0xFF00) << 16);
176#else
177#error Unknown byte order.
178#endif
179 }
180 while (--xCount);
181 }
182}
183
184
185BOOL OOPixMapModulateUniform(OOPixMap *ioPixMap, float f0, float f1, float f2, float f3)
186{
187 if (EXPECT_NOT(ioPixMap == NULL || !OOIsValidPixMap(*ioPixMap))) return NO;
188 if (EXPECT_NOT(!OOPixMapToRGBA(ioPixMap))) return NO;
189
190 ModulateUniform_4(*ioPixMap, f0 * 256.0f, f1 * 256.0f, f2 * 256.0f, f3 * 256.0f);
191
192 return YES;
193}
194
195
196static void ModulateUniform_4(OOPixMap pixMap, uint16_t f3, uint16_t f2, uint16_t f1, uint16_t f0)
197{
198 NSCParameterAssert(OOPixMapBytesPerPixel(pixMap) == 4);
199
200 uint32_t *curr;
201 uint_fast32_t px;
202 uint_fast32_t p0, p1, p2, p3;
203 uint_fast32_t xCount, y;
204
205 for (y = 0; y < pixMap.height; y++)
206 {
207 curr = (uint32_t *)((char *)pixMap.pixels + y * pixMap.rowBytes);
208 xCount = pixMap.width;
209
210 do
211 {
212 px = *curr;
213
214 /* Principle of operation:
215 Each pixel component is in the range 0..0xFF.
216 Each constant factor component is in the range 0..0x100.
217 Multiplying them therefore gives us a result in the range
218 0x0000..0xFF00. The bottom byte is discarded by shifting
219 and masking.
220 */
221
222 p0 = px & 0xFF000000;
223 p0 = ((p0 >> 8) * f0) & 0xFF000000;
224
225 p1 = px & 0x00FF0000;
226 p1 = ((p1 * f1) >> 8) & 0x00FF0000;
227
228 p2 = px & 0x0000FF00;
229 p2 = ((p2 * f2) >> 8) & 0x0000FF00;
230
231 p3 = px & 0x000000FF;
232 p3 = ((p3 * f3) >> 8) & 0x000000FF;
233
234 px = p0 | p1 | p2 | p3;
235 *curr++ = px;
236 }
237 while (--xCount);
238 }
239}
240
241
242BOOL OOPixMapModulatePixMap(OOPixMap *ioDstPixMap, OOPixMap otherPixMap)
243{
244 if (EXPECT_NOT(ioDstPixMap == NULL || !OOIsValidPixMap(*ioDstPixMap))) return NO;
245 if (EXPECT_NOT(!OOIsValidPixMap(otherPixMap) || otherPixMap.format != kOOPixMapRGBA)) return NO;
246 if (EXPECT_NOT(!OOPixMapToRGBA(ioDstPixMap))) return NO;
247 if (EXPECT_NOT(ioDstPixMap->width != otherPixMap.width || ioDstPixMap->height != otherPixMap.height)) return NO;
248
249 ModulatePixMap_4(*ioDstPixMap, otherPixMap);
250
251 return YES;
252}
253
254
255static void ModulatePixMap_4(OOPixMap mainPx, OOPixMap otherPx)
256{
257 uint32_t *dst, *other;
258 uint_fast32_t px;
259 uint_fast16_t m0, m1, m2, m3;
260 uint_fast16_t o0, o1, o2, o3;
261 uint_fast32_t xCount, y;
262
263 for (y = 0; y < mainPx.height; y++)
264 {
265 dst = (uint32_t *)((char *)mainPx.pixels + y * mainPx.rowBytes);
266 other = (uint32_t *)((char *)otherPx.pixels + y * otherPx.rowBytes);
267 xCount = mainPx.width;
268
269 do
270 {
271 px = *dst;
272 m0 = (px >> 24) & 0xFF;
273 m1 = (px >> 16) & 0xFF;
274 m2 = (px >> 8) & 0xFF;
275 m3 = px & 0xFF;
276
277 px = *other;
278 o0 = (px >> 24) & 0xFF;
279 o1 = (px >> 16) & 0xFF;
280 o2 = (px >> 8) & 0xFF;
281 o3 = px & 0xFF;
282
283 /* Unlike in ModulateUniform(), neither side here goes to 256, so
284 we have to divide by 255 rather than shifting. However, the
285 compiler should be able to optimize this to a multiplication
286 by a magic number.
287 */
288 m0 = (m0 * o0) / 255;
289 m1 = (m1 * o1) / 255;
290 m2 = (m2 * o2) / 255;
291 m3 = (m3 * o3) / 255;
292
293 *dst++ = ((uint_fast32_t)m0 << 24) | ((uint_fast32_t)m1 << 16) | (m2 << 8) | m3;
294 other++;
295 }
296 while (--xCount);
297 }
298}
299
300
301BOOL OOPixMapAddPixMap(OOPixMap *ioDstPixMap, OOPixMap otherPixMap)
302{
303 if (EXPECT_NOT(ioDstPixMap == NULL || !OOIsValidPixMap(*ioDstPixMap))) return NO;
304 if (EXPECT_NOT(!OOIsValidPixMap(otherPixMap) || otherPixMap.format != kOOPixMapRGBA)) return NO;
305 if (EXPECT_NOT(!OOPixMapToRGBA(ioDstPixMap))) return NO;
306 if (EXPECT_NOT(ioDstPixMap->width != otherPixMap.width || ioDstPixMap->height != otherPixMap.height)) return NO;
307
308 AddPixMap_4(*ioDstPixMap, otherPixMap);
309
310 return YES;
311}
312
313
314static void AddPixMap_4(OOPixMap mainPx, OOPixMap otherPx)
315{
316 uint32_t *dst, *other;
317 uint_fast32_t px;
318 uint_fast32_t m02, m13;
319 uint_fast32_t o02, o13;
320 uint_fast32_t xCount, y;
321
322 for (y = 0; y < mainPx.height; y++)
323 {
324 dst = (uint32_t *)((char *)mainPx.pixels + y * mainPx.rowBytes);
325 other = (uint32_t *)((char *)otherPx.pixels + y * otherPx.rowBytes);
326 xCount = mainPx.width;
327
328 do
329 {
330 px = *dst;
331 m02 = (px & 0xFF00FF00) >> 8;
332 m13 = px & 0x00FF00FF;
333
334 px = *other;
335 o02 = (px & 0xFF00FF00) >> 8;
336 o13 = px & 0x00FF00FF;
337
338 /* Saturated adds, two components at a time.
339 By masking out the overflow bits of each component,
340 multiplying them by 0xFF and shifting right one byte, we get a
341 mask that's oxFF for components that overflowed and 0x00 for
342 components that did not, without any conditionals.
343 */
344 m02 += o02;
345 m02 |= ((m02 & 0x01000100) * 0xFF) >> 8;
346 m13 += o13;
347 m13 |= ((m13 & 0x01000100) * 0xFF) >> 8;
348
349 *dst++ = ((m02 << 8) & 0xFF00FF00) | (m13 & 0x00FF00FF);
350 other++;
351 }
352 while (--xCount);
353 }
354}
355
#define EXPECT_NOT(x)
static void ToRGBA_2(OOPixMap srcPx, OOPixMap dstPx)
static void ToRGBA_1(OOPixMap srcPx, OOPixMap dstPx)
static void AddPixMap_4(OOPixMap mainPx, OOPixMap otherPx)
BOOL OOPixMapModulateUniform(OOPixMap *ioPixMap, float f0, float f1, float f2, float f3)
BOOL OOExtractPixMapChannel(OOPixMap *ioPixMap, uint8_t channelIndex, BOOL compactWhenDone)
static void ExtractChannel_4(OOPixMap *ioPixMap, uint8_t channelIndex)
BOOL OOPixMapAddPixMap(OOPixMap *ioDstPixMap, OOPixMap otherPixMap)
static void ModulatePixMap_4(OOPixMap mainPx, OOPixMap otherPx)
static void ModulateUniform_4(OOPixMap pixMap, uint16_t f0, uint16_t f1, uint16_t f2, uint16_t f3)
BOOL OOPixMapModulatePixMap(OOPixMap *ioDstPixMap, OOPixMap otherPixMap)
BOOL OOPixMapToRGBA(OOPixMap *ioPixMap)
@ kOOPixMapInvalidFormat
Definition OOPixMap.h:40
@ kOOPixMapGrayscale
Definition OOPixMap.h:41
@ kOOPixMapRGBA
Definition OOPixMap.h:43
@ kOOPixMapGrayscaleAlpha
Definition OOPixMap.h:42
OOPixMap OOAllocatePixMap(OOPixMapDimension width, OOPixMapDimension height, OOPixMapFormat format, size_t rowBytes, size_t bufferSize)
Definition OOPixMap.m:73
OOINLINE unsigned short OOPixMapBytesPerPixel(OOPixMap pixMap)
Definition OOPixMap.h:132
OOINLINE BOOL OOIsNullPixMap(OOPixMap pixMap)
Definition OOPixMap.h:60
BOOL OOIsValidPixMap(OOPixMap pixMap)
Definition OOPixMap.m:42
OOINLINE void OOCompactPixMap(OOPixMap *ioPixMap)
Definition OOPixMap.h:103
float y
OOPixMapDimension height
Definition OOPixMap.h:50
size_t rowBytes
Definition OOPixMap.h:52
void * pixels
Definition OOPixMap.h:49
OOPixMapDimension width
Definition OOPixMap.h:50
OOPixMapFormat format
Definition OOPixMap.h:51