|
1 /* |
|
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Image implementation |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 /*! |
|
20 * \internal |
|
21 * \file |
|
22 * \brief Image implementation |
|
23 * |
|
24 */ |
|
25 |
|
26 #ifndef M3G_CORE_INCLUDE |
|
27 # error included by m3g_core.c; do not compile separately. |
|
28 #endif |
|
29 |
|
30 #include "m3g_image.h" |
|
31 #include "m3g_texture.h" |
|
32 |
|
33 /* Declare prototypes for some of the helper functions called from the |
|
34 * platform-dependent code included below */ |
|
35 |
|
36 static M3Gint m3gBytesPerPixel(M3GPixelFormat format); |
|
37 static M3Gint m3gGetNumMipmapLevels(M3Gint w, M3Gint h); |
|
38 static void m3gDownsample(M3GPixelFormat format, const M3Gubyte *srcPixels, M3Gint *pw, M3Gint *ph, M3Gubyte *dstPixels); |
|
39 static void m3gFreeImageData(Image *img); |
|
40 |
|
41 /* Include platform-dependent functionality */ |
|
42 #include "m3g_image.inl" |
|
43 |
|
44 /* Size of the buffer used in pixel format conversions; this should be |
|
45 * an even number */ |
|
46 #define SPAN_BUFFER_SIZE 32 |
|
47 |
|
48 M3G_CT_ASSERT((SPAN_BUFFER_SIZE & 1) == 0); |
|
49 |
|
50 /*---------------------------------------------------------------------- |
|
51 * Private functions |
|
52 *--------------------------------------------------------------------*/ |
|
53 |
|
54 /*! |
|
55 * \internal |
|
56 * \brief Destroys this Image object. |
|
57 * |
|
58 * \param obj Image object |
|
59 */ |
|
60 static void m3gDestroyImage(Object *obj) |
|
61 { |
|
62 Image *image = (Image*)obj; |
|
63 Interface *m3g = M3G_INTERFACE(image); |
|
64 M3G_VALIDATE_OBJECT(image); |
|
65 |
|
66 if (!image->copyOf) { |
|
67 m3gFreeObject(m3g, image->data); |
|
68 m3gFreeObject(m3g, image->mipData); |
|
69 } |
|
70 M3G_ASSIGN_REF(image->copyOf, NULL); |
|
71 |
|
72 if (image->powerOfTwo != image) { |
|
73 M3G_ASSIGN_REF(image->powerOfTwo, NULL); |
|
74 } |
|
75 |
|
76 # if !defined(M3G_NGL_TEXTURE_API) |
|
77 if (image->texObject) { |
|
78 m3gDeleteGLTextures(m3g, 1, &image->texObject); |
|
79 } |
|
80 if (image->large != NULL) { |
|
81 m3gDestroyLargeImage(image); |
|
82 } |
|
83 # endif |
|
84 |
|
85 m3gDestroyObject(obj); |
|
86 } |
|
87 |
|
88 /*--------------------------------------------------------------------*/ |
|
89 |
|
90 #define RED(argb) (((argb) >> 16) & 0xFFu) |
|
91 #define GREEN(argb) (((argb) >> 8) & 0xFFu) |
|
92 #define BLUE(argb) ( (argb) & 0xFFu) |
|
93 #define ALPHA(argb) (((argb) >> 24) & 0xFFu) |
|
94 |
|
95 #define RGBSUM(argb) (0x4CB2u * RED(argb) + \ |
|
96 0x9691u * GREEN(argb) + \ |
|
97 0x1D3Eu * BLUE(argb)) |
|
98 |
|
99 /*! |
|
100 * \internal \brief ARGB -> A |
|
101 */ |
|
102 static void convertARGBToA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst) |
|
103 { |
|
104 while (count--) { |
|
105 *dst++ = (M3Gubyte) ALPHA(*src++); |
|
106 } |
|
107 } |
|
108 |
|
109 /*! |
|
110 * \internal \brief ARGB -> L |
|
111 */ |
|
112 static void convertARGBToL8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst) |
|
113 { |
|
114 while (count--) { |
|
115 M3Guint argb = *src++; |
|
116 M3Guint sum = RGBSUM(argb); |
|
117 *dst++ = (M3Gubyte)(sum >> 16); |
|
118 } |
|
119 } |
|
120 |
|
121 /*! |
|
122 * \internal \brief ARGB -> LA */ |
|
123 static void convertARGBToLA4(const M3Guint *src, M3Gsizei count, M3Gubyte *dst) |
|
124 { |
|
125 while (count--) { |
|
126 M3Guint argb = *src++; |
|
127 M3Guint sum = RGBSUM(argb); |
|
128 *dst++ = (M3Gubyte)(((sum >> 16) & 0xF0) | ((argb >> 28) & 0x0F)); |
|
129 } |
|
130 } |
|
131 |
|
132 /*! |
|
133 * \internal \brief ARGB -> LA8 |
|
134 */ |
|
135 static void convertARGBToLA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst) |
|
136 { |
|
137 while (count--) { |
|
138 M3Guint argb = *src++; |
|
139 M3Guint sum = RGBSUM(argb); |
|
140 *dst++ = (M3Gubyte)(sum >> 16); /* L */ |
|
141 *dst++ = (M3Gubyte) ALPHA(argb); |
|
142 } |
|
143 } |
|
144 |
|
145 /*! |
|
146 * \internal \brief ARGB -> RGB |
|
147 */ |
|
148 static void convertARGBToRGB8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst) |
|
149 { |
|
150 while (count--) { |
|
151 M3Guint argb = *src++; |
|
152 *dst++ = (M3Gubyte) RED(argb); |
|
153 *dst++ = (M3Gubyte) GREEN(argb); |
|
154 *dst++ = (M3Gubyte) BLUE(argb); |
|
155 } |
|
156 } |
|
157 |
|
158 /*! |
|
159 * \internal \brief ARGB -> RGB565 |
|
160 */ |
|
161 static void convertARGBToRGB565(const M3Guint *src, M3Gsizei count, M3Gubyte *dst) |
|
162 { |
|
163 while (count--) { |
|
164 M3Guint argb = *src++; |
|
165 *(M3Gushort*)dst = (M3Gushort)(((argb >> 8) & 0xF800u)| |
|
166 ((argb >> 5) & 0x07E0u)| |
|
167 ((argb >> 3) & 0x001Fu)); |
|
168 dst += 2; |
|
169 } |
|
170 } |
|
171 |
|
172 /*! |
|
173 * \internal \brief ARGB -> RGBA |
|
174 */ |
|
175 static void convertARGBToRGBA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst) |
|
176 { |
|
177 while (count--) { |
|
178 M3Guint argb = *src++; |
|
179 *dst++ = (M3Gubyte) RED(argb); |
|
180 *dst++ = (M3Gubyte) GREEN(argb); |
|
181 *dst++ = (M3Gubyte) BLUE(argb); |
|
182 *dst++ = (M3Gubyte) ALPHA(argb); |
|
183 } |
|
184 } |
|
185 |
|
186 /*! |
|
187 * \internal \brief ARGB -> BGRA |
|
188 */ |
|
189 static void convertARGBToBGRA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst) |
|
190 { |
|
191 while (count--) { |
|
192 M3Guint argb = *src++; |
|
193 *dst++ = (M3Gubyte) BLUE(argb); |
|
194 *dst++ = (M3Gubyte) GREEN(argb); |
|
195 *dst++ = (M3Gubyte) RED(argb); |
|
196 *dst++ = (M3Gubyte) ALPHA(argb); |
|
197 } |
|
198 } |
|
199 |
|
200 #undef RED |
|
201 #undef GREEN |
|
202 #undef BLUE |
|
203 #undef ALPHA |
|
204 |
|
205 /*! |
|
206 * \internal |
|
207 * \brief Converts a span of ARGB pixels to another format |
|
208 * |
|
209 * \param src source pixels |
|
210 * \param count pixel count |
|
211 * \param dstFormat destination format |
|
212 * \param dst destination pixels |
|
213 */ |
|
214 static void convertFromARGB(const M3Guint *src, |
|
215 M3Gsizei count, |
|
216 M3GPixelFormat dstFormat, |
|
217 M3Gubyte *dst) |
|
218 { |
|
219 switch (dstFormat) { |
|
220 case M3G_L8: |
|
221 convertARGBToL8(src, count, dst); |
|
222 break; |
|
223 case M3G_A8: |
|
224 convertARGBToA8(src, count, dst); |
|
225 break; |
|
226 case M3G_LA4: |
|
227 convertARGBToLA4(src, count, dst); |
|
228 break; |
|
229 case M3G_LA8: |
|
230 convertARGBToLA8(src, count, dst); |
|
231 break; |
|
232 case M3G_RGB8: |
|
233 convertARGBToRGB8(src, count, dst); |
|
234 break; |
|
235 case M3G_RGB565: |
|
236 convertARGBToRGB565(src, count, dst); |
|
237 break; |
|
238 case M3G_RGBA8: |
|
239 case M3G_RGB8_32: |
|
240 convertARGBToRGBA8(src, count, dst); |
|
241 break; |
|
242 case M3G_BGRA8: |
|
243 case M3G_BGR8_32: |
|
244 convertARGBToBGRA8(src, count, dst); |
|
245 break; |
|
246 default: |
|
247 M3G_ASSERT(M3G_FALSE); /* conversion not supported */ |
|
248 } |
|
249 } |
|
250 |
|
251 /*--------------------------------------------------------------------*/ |
|
252 |
|
253 /*! |
|
254 * \internal \brief A8 -> ARGB |
|
255 */ |
|
256 static void convertA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst) |
|
257 { |
|
258 while (count--) { |
|
259 M3Guint argb = M3G_RGB_MASK; |
|
260 argb |= ((M3Guint) *src++) << 24; |
|
261 *dst++ = argb; |
|
262 } |
|
263 } |
|
264 |
|
265 /*! |
|
266 * \internal \brief L8 -> ARGB |
|
267 */ |
|
268 static void convertL8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst) |
|
269 { |
|
270 while (count--) { |
|
271 M3Guint argb = *src++; |
|
272 argb |= (argb << 8) | (argb << 16); |
|
273 argb |= M3G_ALPHA_MASK; |
|
274 *dst++ = argb; |
|
275 } |
|
276 } |
|
277 |
|
278 /*! |
|
279 * \internal \brief LA8 -> ARGB |
|
280 */ |
|
281 static void convertLA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst) |
|
282 { |
|
283 while (count--) { |
|
284 M3Guint argb = *src++; |
|
285 argb |= (argb << 8) | (argb << 16); |
|
286 argb |= ((M3Guint) *src++) << 24; |
|
287 *dst++ = argb; |
|
288 } |
|
289 } |
|
290 |
|
291 /*! |
|
292 * \internal \brief RGB8 -> ARGB |
|
293 */ |
|
294 static void convertRGB8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst) |
|
295 { |
|
296 while (count--) { |
|
297 M3Guint argb = M3G_ALPHA_MASK; |
|
298 argb |= ((M3Guint)(*src++)) << 16; |
|
299 argb |= ((M3Guint)(*src++)) << 8; |
|
300 argb |= (M3Guint)(*src++); |
|
301 *dst++ = argb; |
|
302 } |
|
303 } |
|
304 |
|
305 /*! |
|
306 * \internal \brief RGB565 -> ARGB |
|
307 */ |
|
308 static void convertRGB565ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst) |
|
309 { |
|
310 while (count--) { |
|
311 M3Guint argb = M3G_ALPHA_MASK; |
|
312 const M3Guint rgb565 = *(const M3Gushort*)src; |
|
313 argb |= ((rgb565 & 0xF800u) << 8)|((rgb565 & 0xE000u) << 3); |
|
314 argb |= ((rgb565 & 0x07E0u) << 5)|((rgb565 & 0x0600u) >> 1); |
|
315 argb |= ((rgb565 & 0x001Fu) << 3)|((rgb565 & 0x001Cu) >> 2); |
|
316 *dst++ = argb; |
|
317 src += 2; |
|
318 } |
|
319 } |
|
320 |
|
321 /*! |
|
322 * \internal \brief RGBA8 -> ARGB |
|
323 */ |
|
324 static void convertRGBA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst) |
|
325 { |
|
326 while (count--) { |
|
327 M3Guint argb; |
|
328 argb = ((M3Guint)(*src++)) << 16; |
|
329 argb |= ((M3Guint)(*src++)) << 8; |
|
330 argb |= (M3Guint)(*src++); |
|
331 argb |= ((M3Guint)(*src++)) << 24; |
|
332 *dst++ = argb; |
|
333 } |
|
334 } |
|
335 |
|
336 /*! |
|
337 * \internal \brief BGRA8 -> ARGB |
|
338 */ |
|
339 static void convertBGRA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst) |
|
340 { |
|
341 while (count--) { |
|
342 M3Guint argb; |
|
343 argb = (M3Guint)(*src++); |
|
344 argb |= ((M3Guint)(*src++)) << 8; |
|
345 argb |= ((M3Guint)(*src++)) << 16; |
|
346 argb |= ((M3Guint)(*src++)) << 24; |
|
347 *dst++ = argb; |
|
348 } |
|
349 } |
|
350 |
|
351 /*! |
|
352 * \internal |
|
353 * \brief Converts a span of pixels to ARGB |
|
354 * |
|
355 * \param srcFormat source format |
|
356 * \param src source pixels |
|
357 * \param count pixel count |
|
358 * \param dst destination pixels |
|
359 */ |
|
360 static void convertToARGB(M3GPixelFormat srcFormat, |
|
361 const M3Gubyte *src, |
|
362 M3Gsizei count, |
|
363 M3Guint *dst) |
|
364 { |
|
365 switch (srcFormat) { |
|
366 case M3G_A8: |
|
367 convertA8ToARGB(src, count, dst); |
|
368 break; |
|
369 case M3G_L8: |
|
370 convertL8ToARGB(src, count, dst); |
|
371 break; |
|
372 case M3G_LA8: |
|
373 convertLA8ToARGB(src, count, dst); |
|
374 break; |
|
375 case M3G_RGB8: |
|
376 convertRGB8ToARGB(src, count, dst); |
|
377 break; |
|
378 case M3G_RGB565: |
|
379 convertRGB565ToARGB(src, count, dst); |
|
380 break; |
|
381 case M3G_RGBA8: |
|
382 case M3G_RGB8_32: |
|
383 convertRGBA8ToARGB(src, count, dst); |
|
384 break; |
|
385 case M3G_BGRA8: |
|
386 case M3G_BGR8_32: |
|
387 convertBGRA8ToARGB(src, count, dst); |
|
388 break; |
|
389 default: |
|
390 M3G_ASSERT(M3G_FALSE); /* conversion not supported */ |
|
391 } |
|
392 } |
|
393 |
|
394 /*! |
|
395 * \internal |
|
396 * \brief Fast path for BGRA-to-RGBA conversion |
|
397 */ |
|
398 #if defined (M3G_HW_ARMV6) |
|
399 __asm void fastConvertBGRAToRGBA(const M3Gubyte *src, M3Gsizei srcStride, |
|
400 M3Gsizei width, M3Gsizei height, |
|
401 M3Gubyte *dst) |
|
402 { |
|
403 // r0 = *src |
|
404 // r1 = srcStride |
|
405 // r2 = width |
|
406 // r3 = height |
|
407 // sp[0] = *dst |
|
408 |
|
409 CODE32 |
|
410 |
|
411 CMP r3, #0 // if height = 0, do nothing |
|
412 BXEQ lr |
|
413 |
|
414 STMFD sp!, {r4-r12, lr} |
|
415 |
|
416 LDR r12, [sp, #(10*4)] |
|
417 SUB r1, r1, r2, LSL #2 |
|
418 MOV r14, r2 |
|
419 |
|
420 _fastConvertBGRAToRGBA_outerLoop |
|
421 MOVS r2, r14, ASR #2 // amount of 4x32 bit writes |
|
422 BEQ _fastConvertBGRAToRGBA_tail |
|
423 |
|
424 _fastConvertBGRAToRGBA_innerLoop |
|
425 |
|
426 LDMIA r0!, {r4-r7} // AARRGGBB |
|
427 SUBS r2, #1 |
|
428 REV r4, r4 // BBGGRRAA |
|
429 REV r5, r5 |
|
430 REV r6, r6 |
|
431 REV r7, r7 |
|
432 MOV r8, r4, ROR #8 // AABBGGRR |
|
433 MOV r9, r5, ROR #8 |
|
434 MOV r10, r6, ROR #8 |
|
435 MOV r11, r7, ROR #8 |
|
436 STMIA r12!, {r8-r11} |
|
437 BNE _fastConvertBGRAToRGBA_innerLoop |
|
438 |
|
439 _fastConvertBGRAToRGBA_tail |
|
440 MOV r2, r14, ASR #2 |
|
441 SUBS r2, r14, r2, LSL #2 // number of remaining writes in the tail |
|
442 |
|
443 _fastConvertBGRAToRGBA_tail_loop |
|
444 |
|
445 LDRNE r4, [r0], #4 |
|
446 REVNE r4, r4 |
|
447 MOVNE r8, r4, ROR #8 |
|
448 STRNE r8, [r12], #4 |
|
449 SUBNES r2, #1 |
|
450 BNE _fastConvertBGRAToRGBA_tail_loop |
|
451 |
|
452 SUBS r3, #1 |
|
453 ADD r0, r0, r1 |
|
454 BNE _fastConvertBGRAToRGBA_outerLoop |
|
455 |
|
456 LDMFD sp!, {r4-r12, lr} |
|
457 BX lr |
|
458 |
|
459 } |
|
460 #else /* #if defined (M3G_HW_ARMV6) */ |
|
461 static void fastConvertBGRAToRGBA(const M3Gubyte *src, M3Gsizei srcStride, |
|
462 M3Gsizei width, M3Gsizei height, |
|
463 M3Gubyte *dst) |
|
464 { |
|
465 unsigned int pixel, pixel2; |
|
466 unsigned int temp; |
|
467 unsigned int mask = 0x00ff00ff; |
|
468 int spanwidth = (width >> 1) - 1; |
|
469 int x, y; |
|
470 unsigned int *d = (unsigned int *)dst; |
|
471 |
|
472 M3G_ASSERT(width > 2); |
|
473 |
|
474 for (y = 0; y < height; ++y) { |
|
475 unsigned int *s = (unsigned int *)(src + y*srcStride); |
|
476 |
|
477 pixel = *s++; |
|
478 |
|
479 for (x = 0; x < spanwidth; ++x) { |
|
480 pixel2 = *s++; |
|
481 |
|
482 temp = pixel & mask; /* 00RR00BB */ |
|
483 pixel = pixel - temp; /* AA00GG00 */ |
|
484 pixel = pixel | (temp << 16); /* AABBGG00 */ |
|
485 *d++ = pixel | (temp >> 16); /* AABBGGRR */ |
|
486 |
|
487 pixel = *s++; |
|
488 |
|
489 temp = pixel2 & mask; /* 00RR00BB */ |
|
490 pixel2 = pixel2 - temp; /* AA00GG00 */ |
|
491 pixel2 = pixel2 | (temp << 16); /* AABBGG00 */ |
|
492 *d++ = pixel2 | (temp >> 16); /* AABBGGRR */ |
|
493 } |
|
494 |
|
495 pixel2 = *s++; |
|
496 temp = pixel & mask; /* 00RR00BB */ |
|
497 pixel = pixel - temp; /* AA00GG00 */ |
|
498 pixel = pixel | (temp << 16); /* AABBGG00 */ |
|
499 *d++ = pixel | (temp >> 16); /* AABBGGRR */ |
|
500 |
|
501 temp = pixel2 & mask; /* 00RR00BB */ |
|
502 pixel2 = pixel2 - temp; /* AA00GG00 */ |
|
503 pixel2 = pixel2 | (temp << 16); /* AABBGG00 */ |
|
504 *d++ = pixel2 | (temp >> 16); /* AABBGGRR */ |
|
505 } |
|
506 } |
|
507 #endif /* #if defined (M3G_HW_ARMV6) */ |
|
508 |
|
509 /*--------------------------------------------------------------------*/ |
|
510 |
|
511 /*! |
|
512 * \internal |
|
513 * \brief Maps a logical image format to an internal pixel format |
|
514 * |
|
515 * \param imgFormat logical image format |
|
516 * \param paletted paletted flag |
|
517 * \return the internal image pixel format |
|
518 */ |
|
519 static M3GPixelFormat getInternalFormat(M3GImageFormat imgFormat, |
|
520 M3Gbool paletted) |
|
521 { |
|
522 if (paletted) { |
|
523 switch (imgFormat) { |
|
524 case M3G_RGB: |
|
525 # if defined(M3G_NGL_TEXTURE_API) |
|
526 return M3G_PALETTE8_RGB8_32; |
|
527 # else |
|
528 return M3G_PALETTE8_RGB8; |
|
529 # endif |
|
530 case M3G_RGBA: |
|
531 return M3G_PALETTE8_RGBA8; |
|
532 default: |
|
533 M3G_ASSERT(M3G_FALSE); |
|
534 return (M3GPixelFormat)0; |
|
535 } |
|
536 } |
|
537 else { |
|
538 M3GPixelFormat format = m3gPixelFormat(imgFormat); |
|
539 |
|
540 # if defined(M3G_NGL_TEXTURE_API) |
|
541 if (format == M3G_RGB8) { |
|
542 return (M3G_USE_16BIT_TEXTURES) ? M3G_RGB565 : M3G_RGB8_32; |
|
543 } |
|
544 if (format == M3G_LA8) { |
|
545 return M3G_LA4; |
|
546 } |
|
547 # endif |
|
548 |
|
549 return format; |
|
550 } |
|
551 } |
|
552 |
|
553 /*! |
|
554 * \internal |
|
555 * \brief Gets the correct pixel format for setting data to an image |
|
556 */ |
|
557 static M3GPixelFormat m3gInputDataFormat(const Image *img) |
|
558 { |
|
559 /* Any of the paletted formats will do for a paletted image, as |
|
560 * they all have 8-bit indices; we pick PALETTE8_RGBA8 here */ |
|
561 |
|
562 if (img->flags & M3G_PALETTED) { |
|
563 return M3G_PALETTE8_RGBA8; |
|
564 } |
|
565 |
|
566 return m3gPixelFormat(img->format); |
|
567 } |
|
568 |
|
569 |
|
570 /*! |
|
571 * \internal |
|
572 * \brief Returns log2(resolution)+1. Assumes that resolution is power of two. |
|
573 * |
|
574 * \param w width in pixels |
|
575 * \param h height in pixels |
|
576 * \return number of needed mipmap levels |
|
577 */ |
|
578 static M3Gint m3gGetNumMipmapLevels(M3Gint w, M3Gint h) |
|
579 { |
|
580 M3Gint res = (w > h) ? w : h; |
|
581 M3Gint levels = 0; |
|
582 while (res > 0) { |
|
583 ++levels; |
|
584 res >>= 1; |
|
585 }; |
|
586 return levels; |
|
587 } |
|
588 |
|
589 /*! |
|
590 * \internal |
|
591 * \brief Downsamples an image to half the original size |
|
592 * |
|
593 * |
|
594 * \param format pixel format |
|
595 * \param srcPixels source pixels |
|
596 * \param pw pointer to width |
|
597 * \param ph pointer to height |
|
598 * \param dstPixels destination pixels |
|
599 */ |
|
600 static void m3gDownsample(M3GPixelFormat format, |
|
601 const M3Gubyte *srcPixels, |
|
602 M3Gint *pw, M3Gint *ph, |
|
603 M3Gubyte *dstPixels) |
|
604 { |
|
605 M3Gint i, j, bpp, pixStride, lineStride; |
|
606 M3Gint w = *pw, h = *ph; |
|
607 M3Gubyte *dst; |
|
608 M3Guint temp[2][SPAN_BUFFER_SIZE/2]; |
|
609 |
|
610 M3G_ASSERT_PTR(srcPixels); |
|
611 M3G_ASSERT(w >= 1 && h >= 1); |
|
612 |
|
613 bpp = m3gBytesPerPixel(format); |
|
614 lineStride = (h > 1) ? w * bpp : 0; |
|
615 pixStride = (w > 1) ? bpp : 0; |
|
616 |
|
617 dst = dstPixels; |
|
618 |
|
619 /* Iterate over buffer-sized blocks in the image */ |
|
620 |
|
621 for (j = 0; j < h; j += 2) { |
|
622 for (i = 0; i < w; i += SPAN_BUFFER_SIZE/2) { |
|
623 |
|
624 /* Fill the buffer from the source image */ |
|
625 |
|
626 const M3Gubyte *src = srcPixels + (j*lineStride + i*pixStride); |
|
627 M3Gint c = SPAN_BUFFER_SIZE/2; |
|
628 if (w - i < c) { |
|
629 c = w - i; |
|
630 } |
|
631 convertToARGB(format, src, c, &temp[0][0]); |
|
632 convertToARGB(format, src + lineStride, c, &temp[1][0]); |
|
633 if (w == 1) { |
|
634 temp[0][1] = temp[0][0]; |
|
635 temp[1][1] = temp[1][0]; |
|
636 } |
|
637 |
|
638 /* Average the pixels in the buffer */ |
|
639 { |
|
640 # define AG_MASK 0xFF00FF00u |
|
641 # define RB_MASK 0x00FF00FFu |
|
642 |
|
643 M3Gint k; |
|
644 for (k = 0; k < c; k += 2) { |
|
645 M3Guint ag, rb; |
|
646 |
|
647 /* Add two components in parallel */ |
|
648 |
|
649 ag = ((temp[0][k] & AG_MASK) >> 8) |
|
650 + ((temp[1][k] & AG_MASK) >> 8) |
|
651 + ((temp[0][k+1] & AG_MASK) >> 8) |
|
652 + ((temp[1][k+1] & AG_MASK) >> 8); |
|
653 |
|
654 rb = (temp[0][k] & RB_MASK) |
|
655 + (temp[1][k] & RB_MASK) |
|
656 + (temp[0][k+1] & RB_MASK) |
|
657 + (temp[1][k+1] & RB_MASK); |
|
658 |
|
659 /* Shift to divide by 4, adding ½ for rounding */ |
|
660 |
|
661 temp[0][k>>1] = ((((ag + 0x00020002u) << 6) & AG_MASK) | |
|
662 (((rb + 0x00020002u) >> 2) & RB_MASK)); |
|
663 } |
|
664 |
|
665 # undef AG_MASK |
|
666 # undef RB_MASK |
|
667 } |
|
668 |
|
669 /* Write result to the output buffer */ |
|
670 |
|
671 convertFromARGB(&temp[0][0], c>>1, format, dst); |
|
672 dst += (c>>1) * bpp; |
|
673 } |
|
674 } |
|
675 |
|
676 /* Return output width and height */ |
|
677 |
|
678 if (w > 1) { |
|
679 *pw = (w >> 1); |
|
680 } |
|
681 if (h > 1) { |
|
682 *ph = (h >> 1); |
|
683 } |
|
684 } |
|
685 |
|
686 /*! |
|
687 * \internal |
|
688 * \brief Returns the OpenGL minification filter corresponding to M3G |
|
689 * filtering flags |
|
690 */ |
|
691 static GLenum m3gGetGLMinFilter(M3Genum levelFilter, M3Genum imageFilter) |
|
692 { |
|
693 static const GLenum minFilter[3][2] = { |
|
694 GL_LINEAR, GL_NEAREST, |
|
695 GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_LINEAR, |
|
696 GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_NEAREST |
|
697 }; |
|
698 |
|
699 return minFilter[levelFilter - M3G_FILTER_BASE_LEVEL][imageFilter - M3G_FILTER_LINEAR]; |
|
700 } |
|
701 |
|
702 /*---------------------------------------------------------------------- |
|
703 * Internal functions |
|
704 *--------------------------------------------------------------------*/ |
|
705 |
|
706 /*! |
|
707 * \internal |
|
708 * \brief Converts an internal ARGB color to four GLfixed components |
|
709 */ |
|
710 static void m3gGLColor(M3Guint argb, GLfixed *dst) |
|
711 { |
|
712 GLfixed r, g, b, a; |
|
713 |
|
714 r = (GLfixed)((argb & 0x00FF0000u) >> 16); |
|
715 g = (GLfixed)((argb & 0x0000FF00u) >> 8); |
|
716 b = (GLfixed)( argb & 0x000000FFu ); |
|
717 a = (GLfixed)((argb & 0xFF000000u) >> 24); |
|
718 |
|
719 dst[0] = ((r << 8) | r) + (r >> 7); |
|
720 dst[1] = ((g << 8) | g) + (g >> 7); |
|
721 dst[2] = ((b << 8) | b) + (b >> 7); |
|
722 dst[3] = ((a << 8) | a) + (a >> 7); |
|
723 } |
|
724 |
|
725 /*! |
|
726 * \internal |
|
727 * \brief Binds an image into the current texture unit and sets up |
|
728 * texture filtering |
|
729 */ |
|
730 static void m3gBindTextureImage(Image *img, M3Genum levelFilter, M3Genum imageFilter) |
|
731 { |
|
732 M3G_ASSERT_GL; |
|
733 |
|
734 /* We have no mipmap generation for paletted images, so disable |
|
735 * mipmapping in that case */ |
|
736 |
|
737 if (m3gIsInternallyPaletted(img)) { |
|
738 levelFilter = M3G_FILTER_BASE_LEVEL; |
|
739 } |
|
740 |
|
741 /* Bind the OpenGL texture object, generating mipmaps if |
|
742 * required */ |
|
743 |
|
744 m3gBindTextureObject(img, levelFilter != M3G_FILTER_BASE_LEVEL); |
|
745 |
|
746 /* Set up OpenGL texture filtering according to our flags */ |
|
747 |
|
748 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, |
|
749 (imageFilter == M3G_FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST); |
|
750 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, |
|
751 m3gGetGLMinFilter(levelFilter, imageFilter)); |
|
752 |
|
753 M3G_ASSERT_GL; |
|
754 } |
|
755 |
|
756 /*! |
|
757 * \internal |
|
758 * \brief Maps a logical image format to the matching default pixel |
|
759 * format |
|
760 * |
|
761 * \param imgFormat logical image format |
|
762 * \return a one-byte-per-pixel pixel format |
|
763 */ |
|
764 static M3GPixelFormat m3gPixelFormat(M3GImageFormat imgFormat) |
|
765 { |
|
766 switch (imgFormat) { |
|
767 case M3G_ALPHA: |
|
768 return M3G_A8; |
|
769 case M3G_LUMINANCE: |
|
770 return M3G_L8; |
|
771 case M3G_LUMINANCE_ALPHA: |
|
772 return M3G_LA8; |
|
773 case M3G_RGB: |
|
774 return M3G_RGB8; |
|
775 case M3G_RGBA: |
|
776 return M3G_RGBA8; |
|
777 default: |
|
778 M3G_ASSERT(M3G_FALSE); |
|
779 return M3G_NO_FORMAT; |
|
780 } |
|
781 } |
|
782 |
|
783 /*! |
|
784 * \internal |
|
785 * \brief Returns the number of bytes per pixel in a given pixel format |
|
786 * |
|
787 * \param format pixel format |
|
788 * \return bytes per pixel |
|
789 */ |
|
790 static M3Gint m3gBytesPerPixel(M3GPixelFormat format) |
|
791 { |
|
792 switch (format) { |
|
793 case M3G_L8: |
|
794 case M3G_A8: |
|
795 case M3G_LA4: |
|
796 case M3G_PALETTE8_RGB8: |
|
797 case M3G_PALETTE8_RGB8_32: |
|
798 case M3G_PALETTE8_RGBA8: |
|
799 return 1; |
|
800 case M3G_RGB4: |
|
801 case M3G_RGB565: |
|
802 case M3G_RGBA4: |
|
803 case M3G_RGB5A1: |
|
804 case M3G_LA8: |
|
805 return 2; |
|
806 case M3G_RGB8: |
|
807 return 3; |
|
808 case M3G_RGBA8: |
|
809 case M3G_BGRA8: |
|
810 case M3G_BGR8_32: |
|
811 case M3G_RGB8_32: |
|
812 return 4; |
|
813 default: |
|
814 M3G_ASSERT(M3G_FALSE); |
|
815 return 0; |
|
816 } |
|
817 } |
|
818 |
|
819 /*! |
|
820 * \internal |
|
821 * \brief Converts pixels between formats |
|
822 * |
|
823 * \note Only a limited subset of source and destination formats may |
|
824 * be supported; see the \c convert functions in m3g_image.c |
|
825 * |
|
826 * \param srcFormat source format |
|
827 * \param src source pixels |
|
828 * \param dstFormat destination format |
|
829 * \param dst destination pixels |
|
830 * \param count pixel count |
|
831 */ |
|
832 static void m3gConvertPixels(M3GPixelFormat srcFormat, const M3Gubyte *src, |
|
833 M3GPixelFormat dstFormat, M3Gubyte *dst, |
|
834 M3Gsizei count) |
|
835 { |
|
836 M3Guint temp[SPAN_BUFFER_SIZE]; |
|
837 |
|
838 M3Guint srcBpp = m3gBytesPerPixel(srcFormat); |
|
839 M3Guint dstBpp = m3gBytesPerPixel(dstFormat); |
|
840 M3G_ASSERT(srcBpp > 0 && dstBpp > 0); |
|
841 |
|
842 while (count > 0) { |
|
843 M3Gsizei n = (count < SPAN_BUFFER_SIZE) ? count : SPAN_BUFFER_SIZE; |
|
844 convertToARGB(srcFormat, src, n, temp); |
|
845 convertFromARGB(temp, n, dstFormat, dst); |
|
846 count -= SPAN_BUFFER_SIZE; /* \note may go negative */ |
|
847 src += n * srcBpp; |
|
848 dst += n * dstBpp; |
|
849 } |
|
850 } |
|
851 |
|
852 /*! |
|
853 * \internal |
|
854 * \brief Copies image data. The source image is copied to |
|
855 * the destination image. |
|
856 * |
|
857 * \param dst destination image |
|
858 * \param src source image |
|
859 */ |
|
860 static void m3gCopyImagePixels(Image *dst, |
|
861 const Image *src) |
|
862 { |
|
863 const M3Gubyte *pSrc; |
|
864 M3Gubyte *pDst; |
|
865 M3Gint bpp; |
|
866 |
|
867 /* Check inputs (debug only!) */ |
|
868 M3G_VALIDATE_OBJECT(dst); |
|
869 M3G_VALIDATE_OBJECT(src); |
|
870 |
|
871 M3G_ASSERT(src->internalFormat == dst->internalFormat); |
|
872 M3G_ASSERT(src->format == dst->format); |
|
873 |
|
874 M3G_ASSERT(src->paletteBytes == dst->paletteBytes); |
|
875 |
|
876 /* Compute source and destination pixel data pointers */ |
|
877 pSrc = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(src), src->data); |
|
878 pDst = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(dst), dst->data); |
|
879 |
|
880 bpp = m3gBytesPerPixel(src->internalFormat); |
|
881 |
|
882 if (src->paletteBytes > 0) { |
|
883 m3gCopy(pDst, pSrc, src->paletteBytes); |
|
884 pDst += dst->paletteBytes; |
|
885 pSrc += src->paletteBytes; |
|
886 } |
|
887 |
|
888 /* Do a straight copy if the sizes match, or resample if not */ |
|
889 if (src->width == dst->width && src->height == dst->height ) { |
|
890 m3gCopy(pDst, pSrc, src->width * src->height * bpp); |
|
891 } |
|
892 else { |
|
893 /* Adder values as 8.8 fixed point */ |
|
894 M3Gint xAdd, yAdd; |
|
895 M3Gint x, y; |
|
896 |
|
897 xAdd = (256 * src->width) / dst->width; |
|
898 yAdd = (256 * src->height) / dst->height; |
|
899 |
|
900 for (y = 0; y < dst->height; y++) { |
|
901 for (x = 0; x < dst->width; x++) { |
|
902 m3gCopy(pDst, pSrc + bpp * (((xAdd * x) >> 8) + ((yAdd * y) >> 8) * src->width), bpp); |
|
903 pDst += bpp; |
|
904 } |
|
905 } |
|
906 } |
|
907 |
|
908 m3gUnmapObject(M3G_INTERFACE(dst), dst->data); |
|
909 m3gUnmapObject(M3G_INTERFACE(src), src->data); |
|
910 |
|
911 m3gInvalidateImage(dst); |
|
912 } |
|
913 |
|
914 /*! |
|
915 * \internal |
|
916 * \brief Invalidates any cached data for this image |
|
917 * |
|
918 * Used when rendering to the image. |
|
919 * |
|
920 * \param img Image object |
|
921 */ |
|
922 static void m3gInvalidateImage(Image *img) |
|
923 { |
|
924 M3G_VALIDATE_OBJECT(img); |
|
925 img->dirty = M3G_TRUE; |
|
926 |
|
927 # if !defined(M3G_NGL_TEXTURE_API) |
|
928 if (img->large) { |
|
929 img->large->dirty = M3G_TRUE; |
|
930 } |
|
931 # endif /*M3G_NGL_TEXTURE_API*/ |
|
932 |
|
933 if (img->powerOfTwo != img) { |
|
934 img->powerOfTwoDirty = M3G_TRUE; |
|
935 } |
|
936 } |
|
937 |
|
938 /*! |
|
939 * \internal |
|
940 * \brief Overloaded Object3D method. |
|
941 * |
|
942 * \param originalObj original Image object |
|
943 * \param cloneObj pointer to cloned Image object |
|
944 * \param pairs array for all object-duplicate pairs |
|
945 * \param numPairs number of pairs |
|
946 */ |
|
947 static M3Gbool m3gImageDuplicate(const Object *originalObj, |
|
948 Object **cloneObj, |
|
949 Object **pairs, |
|
950 M3Gint *numPairs) |
|
951 { |
|
952 Image *original = (Image *)originalObj; |
|
953 Image *clone; |
|
954 |
|
955 /* If the original image still has its pixel data, make a full |
|
956 * copy -- this is wasteful for immutable images, but the shame's |
|
957 * on the user in that case */ |
|
958 |
|
959 if (original->data) { |
|
960 clone = (Image*) m3gCreateImage(originalObj->interface, |
|
961 original->format, |
|
962 original->width, |
|
963 original->height, |
|
964 original->flags); |
|
965 } |
|
966 else { |
|
967 |
|
968 /* Otherwise, just point to the original and use its data |
|
969 * buffers */ |
|
970 |
|
971 clone = (Image*) m3gAlloc(M3G_INTERFACE(original), sizeof(*clone)); |
|
972 *clone = *original; |
|
973 M3G_ASSIGN_REF(clone->copyOf, original); |
|
974 } |
|
975 |
|
976 *cloneObj = (Object *)clone; |
|
977 if (*cloneObj == NULL) { |
|
978 return M3G_FALSE; |
|
979 } |
|
980 |
|
981 if (m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) { |
|
982 /* Copy image contents */ |
|
983 if (original->data) { |
|
984 m3gCopyImagePixels(clone, original); |
|
985 } |
|
986 return M3G_TRUE; |
|
987 } |
|
988 else { |
|
989 return M3G_FALSE; |
|
990 } |
|
991 } |
|
992 |
|
993 /*! |
|
994 * \internal |
|
995 * |
|
996 * \brief Frees the pixel data associated with this image; used for |
|
997 * optimizing memory usage after copying the data to a secondary |
|
998 * location |
|
999 */ |
|
1000 static void m3gFreeImageData(Image *img) |
|
1001 { |
|
1002 M3G_ASSERT(img->format == M3G_RGB || img->format == M3G_LUMINANCE); |
|
1003 # if !defined(M3G_NGL_TEXTURE_API) |
|
1004 M3G_ASSERT(!img->mipmapsDirty); |
|
1005 # endif |
|
1006 M3G_ASSERT(!img->powerOfTwoDirty); |
|
1007 M3G_ASSERT(img->powerOfTwo != NULL); |
|
1008 M3G_ASSERT(!img->pinned); |
|
1009 |
|
1010 M3G_LOG1(M3G_LOG_IMAGES, "Freeing copy of image 0x%08X\n", |
|
1011 (unsigned) img); |
|
1012 |
|
1013 if (!img->copyOf) { |
|
1014 m3gFreeObject(M3G_INTERFACE(img), img->data); |
|
1015 img->data = 0; |
|
1016 m3gFreeObject(M3G_INTERFACE(img), img->mipData); |
|
1017 img->mipData = 0; |
|
1018 } |
|
1019 M3G_ASSIGN_REF(img->copyOf, NULL); |
|
1020 } |
|
1021 |
|
1022 /*! |
|
1023 * \internal |
|
1024 * \brief Returns a power-of-two variant of an image |
|
1025 * |
|
1026 * This is used for sprites and background images. |
|
1027 */ |
|
1028 static Image *m3gGetPowerOfTwoImage(Image *img) |
|
1029 { |
|
1030 M3G_VALIDATE_OBJECT(img); |
|
1031 |
|
1032 /* Create a power-of-two variant of the image if one doesn't exist |
|
1033 * already */ |
|
1034 |
|
1035 if (img->powerOfTwo == NULL) { |
|
1036 |
|
1037 M3Gint width, height; |
|
1038 M3Gbitmask flags; |
|
1039 Image *potImage; |
|
1040 |
|
1041 M3G_ASSERT(!m3gIsPowerOfTwo(img->width) || |
|
1042 !m3gIsPowerOfTwo(img->height)); |
|
1043 |
|
1044 /* Choose new image size to allow a maximum shrinkage of 25%; |
|
1045 * this is to weed out pathological cases of quadruple memory |
|
1046 * usage because an image is one pixel too wide */ |
|
1047 |
|
1048 width = m3gNextPowerOfTwo((img->width * 3) >> 2); |
|
1049 height = m3gNextPowerOfTwo((img->height * 3) >> 2); |
|
1050 |
|
1051 width = M3G_MIN(width, M3G_MAX_TEXTURE_DIMENSION); |
|
1052 height = M3G_MIN(height, M3G_MAX_TEXTURE_DIMENSION); |
|
1053 |
|
1054 flags = img->flags & (~M3G_RENDERING_TARGET); |
|
1055 |
|
1056 potImage = m3gCreateImage(M3G_INTERFACE(img), |
|
1057 img->format, |
|
1058 width, height, |
|
1059 flags); |
|
1060 if (!potImage) { |
|
1061 return NULL; /* automatic out-of-memory */ |
|
1062 } |
|
1063 |
|
1064 M3G_ASSIGN_REF(img->powerOfTwo, potImage); |
|
1065 img->powerOfTwoDirty = M3G_TRUE; |
|
1066 } |
|
1067 |
|
1068 /* Update POT image data if necessary */ |
|
1069 |
|
1070 if (img->powerOfTwoDirty) { |
|
1071 m3gCopyImagePixels(img->powerOfTwo, img); |
|
1072 img->powerOfTwoDirty = M3G_FALSE; |
|
1073 |
|
1074 /* Get rid of the original at this point if we can */ |
|
1075 |
|
1076 if (!img->pinned) { |
|
1077 m3gFreeImageData(img); |
|
1078 } |
|
1079 } |
|
1080 |
|
1081 return img->powerOfTwo; |
|
1082 } |
|
1083 |
|
1084 /*! |
|
1085 * \internal |
|
1086 * \brief Gets image alpha at x, y. |
|
1087 * |
|
1088 * \param image Image object |
|
1089 * \param x x-coordinate |
|
1090 * \param y y-coordinate |
|
1091 * \return alpha value |
|
1092 * |
|
1093 */ |
|
1094 static M3Gint m3gGetAlpha(Image *image, M3Gint x, M3Gint y) |
|
1095 { |
|
1096 M3Gint alpha = 255; |
|
1097 M3Gint bpp = m3gBytesPerPixel(image->internalFormat); |
|
1098 M3Guint data = 0; |
|
1099 M3Gubyte *pixels; |
|
1100 |
|
1101 /* Quick exit for non-alpha formats */ |
|
1102 |
|
1103 if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) { |
|
1104 return alpha; |
|
1105 } |
|
1106 |
|
1107 /* For other formats, we have to sample the image data */ |
|
1108 |
|
1109 if (!image->data) { |
|
1110 Image *potImage = image->powerOfTwo; |
|
1111 M3G_ASSERT(potImage != image); |
|
1112 return m3gGetAlpha(potImage, |
|
1113 (x * image->width) / potImage->width, |
|
1114 (y * image->height) / potImage->height); |
|
1115 } |
|
1116 |
|
1117 pixels = ((M3Gubyte *)m3gMapObject(M3G_INTERFACE(image), image->data)); |
|
1118 |
|
1119 if (image->paletteBytes == 0) { |
|
1120 if (bpp == 1) { |
|
1121 data = pixels[x + y * image->width]; |
|
1122 } |
|
1123 else if (bpp == 2) { |
|
1124 data = ((M3Gushort *)pixels)[x + y * image->width]; |
|
1125 } |
|
1126 else { |
|
1127 data = ((M3Guint *)pixels)[x + y * image->width]; |
|
1128 } |
|
1129 } |
|
1130 else { |
|
1131 M3Guint *palette; |
|
1132 palette = (M3Guint *)pixels; |
|
1133 pixels += image->paletteBytes; |
|
1134 |
|
1135 data = palette[pixels[x + y * image->width]]; |
|
1136 } |
|
1137 |
|
1138 m3gUnmapObject(M3G_INTERFACE(image), image->data); |
|
1139 |
|
1140 switch (image->internalFormat) { |
|
1141 |
|
1142 case M3G_A8: |
|
1143 alpha = data; |
|
1144 break; |
|
1145 case M3G_LA8: |
|
1146 alpha = data >> 8; |
|
1147 break; |
|
1148 case M3G_RGBA8: |
|
1149 alpha = data >> 24; |
|
1150 break; |
|
1151 default: |
|
1152 /* Should never be here!! */ |
|
1153 M3G_ASSERT(M3G_FALSE); |
|
1154 } |
|
1155 |
|
1156 return alpha; |
|
1157 } |
|
1158 |
|
1159 /*! |
|
1160 * \internal |
|
1161 * \brief Computes the scanline stride of an image |
|
1162 */ |
|
1163 static M3Gsizei m3gGetImageStride(const Image *img) |
|
1164 { |
|
1165 M3G_VALIDATE_OBJECT(img); |
|
1166 return img->width * m3gBytesPerPixel(img->internalFormat); |
|
1167 } |
|
1168 |
|
1169 /*---------------------------------------------------------------------- |
|
1170 * Virtual function table |
|
1171 *--------------------------------------------------------------------*/ |
|
1172 |
|
1173 static const ObjectVFTable m3gvf_Image = { |
|
1174 m3gObjectApplyAnimation, |
|
1175 m3gObjectIsCompatible, |
|
1176 m3gObjectUpdateProperty, |
|
1177 m3gObjectDoGetReferences, |
|
1178 m3gObjectFindID, |
|
1179 m3gImageDuplicate, |
|
1180 m3gDestroyImage |
|
1181 }; |
|
1182 |
|
1183 |
|
1184 /*---------------------------------------------------------------------- |
|
1185 * Public API functions |
|
1186 *--------------------------------------------------------------------*/ |
|
1187 |
|
1188 /*! |
|
1189 * \brief Creates a new Image |
|
1190 * |
|
1191 * \param interface M3G interface |
|
1192 * \param srcFormat source format |
|
1193 * \param width width in pixels |
|
1194 * \param height height in pixels |
|
1195 * \param flags creation flags; a combination of |
|
1196 * M3G_DYNAMIC, M3G_STATIC, |
|
1197 * M3G_RENDERING_TARGET, and M3G_PALETTED |
|
1198 * \retval Image new Image object |
|
1199 * \retval NULL Image creating failed |
|
1200 */ |
|
1201 M3G_API M3GImage m3gCreateImage(/*@dependent@*/ M3GInterface interface, |
|
1202 M3GImageFormat srcFormat, |
|
1203 M3Gint width, M3Gint height, |
|
1204 M3Gbitmask flags) |
|
1205 { |
|
1206 Interface *m3g = (Interface *) interface; |
|
1207 M3G_VALIDATE_INTERFACE(m3g); |
|
1208 |
|
1209 /* Check errors */ |
|
1210 |
|
1211 if (width <= 0 || height <= 0) { |
|
1212 m3gRaiseError(m3g, M3G_INVALID_VALUE); |
|
1213 return NULL; |
|
1214 } |
|
1215 |
|
1216 if (!m3gInRange(srcFormat, M3G_ALPHA, M3G_RGBA)) { |
|
1217 m3gRaiseError(m3g, M3G_INVALID_ENUM); |
|
1218 return NULL; |
|
1219 } |
|
1220 |
|
1221 /* Parameters OK; allocate and initialize the object */ |
|
1222 |
|
1223 { |
|
1224 Image *img = m3gAllocZ(m3g, sizeof(Image)); |
|
1225 if (img == NULL) { |
|
1226 return NULL; |
|
1227 } |
|
1228 |
|
1229 /* Clean up and set flags */ |
|
1230 |
|
1231 M3G_LOG3(M3G_LOG_IMAGES, "Image 0x%08X is %d x %d", |
|
1232 (unsigned) img, width, height); |
|
1233 |
|
1234 flags |= M3G_DYNAMIC; /* the default */ |
|
1235 |
|
1236 if (flags & M3G_STATIC) { |
|
1237 M3G_LOG(M3G_LOG_IMAGES, ", immutable"); |
|
1238 flags &= ~M3G_DYNAMIC; |
|
1239 } |
|
1240 if (flags & M3G_RENDERING_TARGET) { |
|
1241 M3G_LOG(M3G_LOG_IMAGES, ", rendertarget"); |
|
1242 flags |= M3G_DYNAMIC; |
|
1243 } |
|
1244 if (flags & M3G_PALETTED) { |
|
1245 M3G_LOG(M3G_LOG_IMAGES, ", paletted"); |
|
1246 } |
|
1247 img->flags = flags; |
|
1248 |
|
1249 M3G_LOG(M3G_LOG_IMAGES, "\n"); |
|
1250 |
|
1251 { |
|
1252 /* Allocate pixel & palette data; the palette is stored at |
|
1253 * the beginning of the pixel data chunk */ |
|
1254 |
|
1255 /* \ comment */ |
|
1256 M3Gbool paletted = ((img->flags & M3G_PALETTED) != 0) |
|
1257 && m3gSupportedPaletteFormat(srcFormat); |
|
1258 M3GPixelFormat internalFormat = getInternalFormat(srcFormat, |
|
1259 paletted); |
|
1260 M3Guint bpp = m3gBytesPerPixel(internalFormat); |
|
1261 M3Guint pixelBytes = width * height * bpp; |
|
1262 |
|
1263 if ((img->flags & M3G_PALETTED) != 0 && !paletted) { |
|
1264 M3G_LOG(M3G_LOG_WARNINGS|M3G_LOG_IMAGES, |
|
1265 "Warning: Unsupported paletted format\n"); |
|
1266 } |
|
1267 |
|
1268 /* The palette will always have 256 elements and one byte |
|
1269 * per color component (except padded 32-bit for NGL) */ |
|
1270 |
|
1271 if (paletted) { |
|
1272 img->paletteBytes = |
|
1273 # if defined(M3G_NGL_TEXTURE_API) |
|
1274 256 * 4; |
|
1275 # else |
|
1276 256 * m3gBytesPerPixel(m3gPixelFormat(srcFormat)); |
|
1277 # endif |
|
1278 } |
|
1279 |
|
1280 /* Set up the rest of the image parameters */ |
|
1281 |
|
1282 img->width = width; |
|
1283 img->height = height; |
|
1284 img->format = srcFormat; |
|
1285 img->internalFormat = internalFormat; |
|
1286 img->glFormat = m3gGetGLFormat(internalFormat); |
|
1287 |
|
1288 M3G_LOG1(M3G_LOG_IMAGES, "Image data %d bytes\n", |
|
1289 pixelBytes + img->paletteBytes); |
|
1290 |
|
1291 /* Allocate the image memory */ |
|
1292 |
|
1293 img->data = m3gAllocObject(m3g, pixelBytes + img->paletteBytes); |
|
1294 if (img->data == 0) { |
|
1295 m3gFree(m3g, img); |
|
1296 return NULL; |
|
1297 } |
|
1298 |
|
1299 /* Lock the image data in memory if the image is dynamic, |
|
1300 * or the format has alpha information; otherwise, we'll |
|
1301 * be able to get rid of an extra copy when generating a |
|
1302 * power-of-two version or uploading to OpenGL */ |
|
1303 |
|
1304 if ((img->flags & M3G_DYNAMIC) != 0 |
|
1305 || (img->format != M3G_RGB && |
|
1306 img->format != M3G_LUMINANCE)) { |
|
1307 img->pinned = M3G_TRUE; |
|
1308 } |
|
1309 |
|
1310 /* If the image can be used as a rendering target, clear |
|
1311 * to opaque white by default */ |
|
1312 |
|
1313 if ((img->flags & M3G_RENDERING_TARGET) != 0) { |
|
1314 M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data)) |
|
1315 + img->paletteBytes; |
|
1316 m3gFill(pixels, (size_t) pixelBytes, -1); |
|
1317 m3gUnmapObject(m3g, img->data); |
|
1318 } |
|
1319 |
|
1320 /* Check for "special" images that can't be used as |
|
1321 * textures without some extra trickery */ |
|
1322 |
|
1323 if (!m3gIsPowerOfTwo((M3Guint) width) || |
|
1324 !m3gIsPowerOfTwo((M3Guint) height)) { |
|
1325 img->special |= IMG_NPOT; |
|
1326 } |
|
1327 else { |
|
1328 img->powerOfTwo = img; |
|
1329 } |
|
1330 |
|
1331 if (width > M3G_MAX_TEXTURE_DIMENSION || |
|
1332 height > M3G_MAX_TEXTURE_DIMENSION) { |
|
1333 img->special |= IMG_LARGE; |
|
1334 } |
|
1335 } |
|
1336 |
|
1337 /* Call base class constructor (can not fail) and return */ |
|
1338 m3gInitObject(&img->object, m3g, M3G_CLASS_IMAGE); |
|
1339 |
|
1340 M3G_VALIDATE_OBJECT(img); |
|
1341 return (M3GImage) img; |
|
1342 } |
|
1343 } |
|
1344 |
|
1345 /*! |
|
1346 * \brief Prevents further modifications to an image |
|
1347 * |
|
1348 * Essentially, this changes the default M3G_DYNAMIC flag to |
|
1349 * M3G_STATIC; this allows the implementation to make memory and |
|
1350 * performance optimizations not possible for dynamically modified |
|
1351 * images. |
|
1352 */ |
|
1353 M3G_API void m3gCommitImage(M3GImage hImage) |
|
1354 { |
|
1355 Image *image = (Image *) hImage; |
|
1356 M3Gbitmask flags; |
|
1357 M3G_VALIDATE_OBJECT(image); |
|
1358 |
|
1359 flags = image->flags; |
|
1360 flags &= ~(M3G_DYNAMIC|M3G_RENDERING_TARGET); |
|
1361 flags |= M3G_STATIC; |
|
1362 |
|
1363 image->flags = flags; |
|
1364 |
|
1365 /* If the image format has no alpha information, we can discard |
|
1366 * the image data under suitable conditions */ |
|
1367 |
|
1368 if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) { |
|
1369 image->pinned = M3G_FALSE; |
|
1370 } |
|
1371 |
|
1372 M3G_LOG1(M3G_LOG_IMAGES, "Image 0x%08X made immutable\n", |
|
1373 (unsigned) image); |
|
1374 } |
|
1375 |
|
1376 /*! |
|
1377 * \brief Check if image is mutable. |
|
1378 * |
|
1379 * \param hImage Image object |
|
1380 * \retval M3G_TRUE image is mutable |
|
1381 * \retval M3G_FALSE image is immutable |
|
1382 */ |
|
1383 M3G_API M3Gbool m3gIsMutable(M3GImage hImage) |
|
1384 { |
|
1385 Image *image = (Image *) hImage; |
|
1386 M3G_VALIDATE_OBJECT(image); |
|
1387 return ((image->flags & M3G_DYNAMIC) != 0); |
|
1388 } |
|
1389 |
|
1390 /*! |
|
1391 * \brief Gets image format as JSR-184 constant |
|
1392 * |
|
1393 * \param hImage Image object |
|
1394 * \return JSR-184 format |
|
1395 */ |
|
1396 M3G_API M3GImageFormat m3gGetFormat(M3GImage hImage) |
|
1397 { |
|
1398 Image *image = (Image *) hImage; |
|
1399 M3G_VALIDATE_OBJECT(image); |
|
1400 return image->format; |
|
1401 } |
|
1402 |
|
1403 /*! |
|
1404 * \brief Gets image width |
|
1405 * |
|
1406 * \param hImage Image object |
|
1407 * \return width in pixels |
|
1408 */ |
|
1409 M3G_API M3Gint m3gGetWidth(M3GImage hImage) |
|
1410 { |
|
1411 Image *image = (Image *) hImage; |
|
1412 M3G_VALIDATE_OBJECT(image); |
|
1413 return image->width; |
|
1414 } |
|
1415 |
|
1416 /*! |
|
1417 * \brief Gets image height |
|
1418 * |
|
1419 * \param hImage Image object |
|
1420 * \return height in pixels |
|
1421 */ |
|
1422 M3G_API M3Gint m3gGetHeight(M3GImage hImage) |
|
1423 { |
|
1424 Image *image = (Image *) hImage; |
|
1425 M3G_VALIDATE_OBJECT(image); |
|
1426 return image->height; |
|
1427 } |
|
1428 |
|
1429 /*! |
|
1430 * \brief Converts a rectangle of pixels of src to dst as srcFormat to |
|
1431 * dstFormat conversion requires. |
|
1432 * |
|
1433 * \param srcFormat source format |
|
1434 * \param src source pixels |
|
1435 * \param srcStride source stride |
|
1436 * \param width width in pixels |
|
1437 * \param height height in pixels |
|
1438 * \param dstFormat destination format |
|
1439 * \param dst destination pixels |
|
1440 * \param dstStride destination stride |
|
1441 */ |
|
1442 static void m3gConvertPixelRect( |
|
1443 M3GPixelFormat srcFormat, const M3Gubyte *src, M3Gsizei srcStride, |
|
1444 M3Gsizei width, M3Gsizei height, |
|
1445 M3GPixelFormat dstFormat, M3Gubyte *dst, M3Gsizei dstStride) |
|
1446 { |
|
1447 /* Detect any fast path cases */ |
|
1448 |
|
1449 if ((srcFormat == M3G_BGRA8 || srcFormat == M3G_BGR8_32) |
|
1450 && dstFormat == M3G_RGBA8) { |
|
1451 if (width > 2 && dstStride == width*4) { |
|
1452 |
|
1453 const char endianTest[4] = { 1, 0, 0, 0 }; |
|
1454 if ((*(const int *)endianTest) == 1) { |
|
1455 fastConvertBGRAToRGBA(src, srcStride, width, height, dst); |
|
1456 } |
|
1457 return; |
|
1458 } |
|
1459 } |
|
1460 |
|
1461 /* No luck, do the generic conversion */ |
|
1462 |
|
1463 while (height-- > 0) { |
|
1464 m3gConvertPixels(srcFormat, src, dstFormat, dst, width); |
|
1465 src += srcStride; |
|
1466 dst += dstStride; |
|
1467 } |
|
1468 } |
|
1469 |
|
1470 /*! |
|
1471 * \brief Sets the pixel data for an image |
|
1472 * |
|
1473 * \param hImage Image object |
|
1474 * \param srcPixels source pixels |
|
1475 */ |
|
1476 M3G_API void m3gSetImage(M3GImage hImage, const void *srcPixels) |
|
1477 { |
|
1478 Image *img = (Image *) hImage; |
|
1479 M3G_VALIDATE_OBJECT(img); |
|
1480 |
|
1481 { |
|
1482 M3Gsizei bpp = m3gBytesPerPixel(m3gInputDataFormat(img)); |
|
1483 m3gSetSubImage(hImage, |
|
1484 0, 0, img->width, img->height, |
|
1485 img->width * img->height * bpp, srcPixels); |
|
1486 } |
|
1487 } |
|
1488 |
|
1489 /*! |
|
1490 * \brief Reads pixel data from an image |
|
1491 * |
|
1492 * \param hImage Image object |
|
1493 * \param pixels output buffer for pixels |
|
1494 */ |
|
1495 M3G_API void m3gGetImageARGB(M3GImage hImage, M3Guint *pixels) |
|
1496 { |
|
1497 Interface *m3g; |
|
1498 const Image *img = (const Image *) hImage; |
|
1499 M3G_VALIDATE_OBJECT(img); |
|
1500 m3g = M3G_INTERFACE(img); |
|
1501 |
|
1502 if (!pixels) { |
|
1503 m3gRaiseError(m3g, M3G_NULL_POINTER); |
|
1504 return; |
|
1505 } |
|
1506 |
|
1507 if (img->data) { |
|
1508 const M3Gubyte *src = (const M3Gubyte*) m3gMapObject(m3g, img->data); |
|
1509 convertToARGB(img->internalFormat, src, |
|
1510 img->width * img->height, |
|
1511 pixels); |
|
1512 m3gUnmapObject(m3g, img->data); |
|
1513 } |
|
1514 } |
|
1515 |
|
1516 /*! |
|
1517 * \brief Sets the palette for an image |
|
1518 * |
|
1519 * \param hImage Image object |
|
1520 * \param paletteLength length of the palette |
|
1521 * \param srcPalette palette data |
|
1522 */ |
|
1523 M3G_API void m3gSetImagePalette(M3GImage hImage, |
|
1524 M3Gint paletteLength, |
|
1525 const void *srcPalette) |
|
1526 { |
|
1527 Interface *m3g; |
|
1528 Image *img = (Image *) hImage; |
|
1529 M3G_VALIDATE_OBJECT(img); |
|
1530 m3g = M3G_INTERFACE(img); |
|
1531 |
|
1532 /* Check for errors */ |
|
1533 |
|
1534 if (img->data == 0 || (img->flags & M3G_STATIC) != 0 |
|
1535 || (img->flags & M3G_PALETTED) == 0) { |
|
1536 M3G_ASSERT(!(img->flags & M3G_DYNAMIC)); |
|
1537 m3gRaiseError(m3g, M3G_INVALID_OPERATION); |
|
1538 return; |
|
1539 } |
|
1540 if (srcPalette == NULL) { |
|
1541 m3gRaiseError(m3g, M3G_NULL_POINTER); |
|
1542 return; |
|
1543 } |
|
1544 if (!m3gInRange(paletteLength, 0, 256)) { |
|
1545 m3gRaiseError(m3g, M3G_INVALID_VALUE); |
|
1546 return; |
|
1547 } |
|
1548 |
|
1549 /* |
|
1550 * Copy the palette data into the allocated palette (for natively |
|
1551 * supported paletted formats), or remap the existing image data |
|
1552 * using the supplied palette entries (for non-native formats) |
|
1553 * |
|
1554 * NOTE the latter is a one-time operation! |
|
1555 */ |
|
1556 if (img->paletteBytes > 0) { |
|
1557 M3Gubyte *palette = (M3Gubyte *)m3gMapObject(m3g, img->data); |
|
1558 # if defined(M3G_NGL_TEXTURE_API) |
|
1559 m3gConvertPixels(m3gPixelFormat(img->format), srcPalette, |
|
1560 M3G_RGBA8, palette, |
|
1561 paletteLength); |
|
1562 # else |
|
1563 M3Gsizei bpp = m3gBytesPerPixel(m3gPixelFormat(img->format)); |
|
1564 m3gCopy(palette, srcPalette, (size_t) paletteLength * bpp); |
|
1565 # endif |
|
1566 } |
|
1567 else { |
|
1568 M3Gint count = img->width * img->height; |
|
1569 M3Gubyte *pixel = (M3Gubyte*)m3gMapObject(m3g, img->data); |
|
1570 const M3Gubyte *bytePalette = (const M3Gubyte *) srcPalette; |
|
1571 |
|
1572 /* We need to treat the input and internal formats as |
|
1573 * separate, as the internal storage may be padded to more |
|
1574 * bytes than there are color components */ |
|
1575 |
|
1576 M3GPixelFormat paletteFormat = m3gPixelFormat(img->format); |
|
1577 const int numComponents = m3gBytesPerPixel(paletteFormat); |
|
1578 M3GPixelFormat imgFormat = img->internalFormat; |
|
1579 const int imgBpp = m3gBytesPerPixel(imgFormat); |
|
1580 |
|
1581 /* In most cases we can just copy the corresponding palette |
|
1582 * entry on top of each pixel based on the pixel intensity (R |
|
1583 * or L component), but special formats require a more |
|
1584 * complicated conversion. We just use the (slow) general |
|
1585 * conversion routine, as it already incorporates support for |
|
1586 * all formats. */ |
|
1587 |
|
1588 if (imgBpp >= numComponents) { |
|
1589 while (count--) { |
|
1590 int offset = (*pixel) * numComponents; |
|
1591 int c; |
|
1592 for (c = 0; c < numComponents; ++c) { |
|
1593 *pixel++ = bytePalette[offset + c]; |
|
1594 } |
|
1595 while (c++ < imgBpp) { /* padding for e.g. 24-bit RGB */ |
|
1596 *pixel++ = 0xFF; |
|
1597 } |
|
1598 } |
|
1599 } |
|
1600 else { |
|
1601 while (count--) { |
|
1602 int offset = (*pixel) * numComponents; |
|
1603 m3gConvertPixels(paletteFormat, &bytePalette[offset], |
|
1604 imgFormat, pixel, |
|
1605 1); |
|
1606 pixel += imgBpp; |
|
1607 } |
|
1608 } |
|
1609 } |
|
1610 m3gUnmapObject(m3g, img->data); |
|
1611 m3gInvalidateImage(img); |
|
1612 } |
|
1613 |
|
1614 /*! |
|
1615 * \brief Sets a scanline of an image |
|
1616 * |
|
1617 * \param hImage Image object |
|
1618 * \param line scanline |
|
1619 * \param trueAlpha M3G_TRUE if the source image has an alpha channel, |
|
1620 * M3G_FALSE if it should come from the RGB values; |
|
1621 * this only matters for alpha-only destination images |
|
1622 * \param pixels souce pixels |
|
1623 */ |
|
1624 M3G_API void m3gSetImageScanline(M3GImage hImage, |
|
1625 M3Gint line, |
|
1626 M3Gbool trueAlpha, |
|
1627 const M3Guint *pixels) |
|
1628 { |
|
1629 Image *img = (Image *) hImage; |
|
1630 M3G_VALIDATE_OBJECT(img); |
|
1631 |
|
1632 if (img->data == 0 || (img->flags & M3G_STATIC) != 0 |
|
1633 || img->paletteBytes != 0) { |
|
1634 m3gRaiseError(M3G_INTERFACE(img), M3G_INVALID_OPERATION); |
|
1635 return; |
|
1636 } |
|
1637 |
|
1638 { |
|
1639 Interface *m3g = M3G_INTERFACE(img); |
|
1640 M3Gint stride = img->width * m3gBytesPerPixel(img->internalFormat); |
|
1641 M3Gubyte *dst = ((M3Gubyte *) m3gMapObject(m3g, img->data)) |
|
1642 + img->paletteBytes; |
|
1643 |
|
1644 #ifdef M3G_NGL_TEXTURE_API |
|
1645 /* For RGB images without alpha channel, source alpha is |
|
1646 * forced to 0xff. */ |
|
1647 |
|
1648 if (img->format == M3G_RGB) { |
|
1649 M3Gint i; |
|
1650 M3Guint argb, *dst; |
|
1651 |
|
1652 dst = (M3Guint *) pixels; |
|
1653 |
|
1654 for (i = 0; i < img->width; i++) { |
|
1655 argb = *dst | 0xff000000; |
|
1656 *dst++ = argb; |
|
1657 } |
|
1658 } |
|
1659 #endif |
|
1660 |
|
1661 /* Note that an alpha-only destination format is faked for |
|
1662 * luminance if the source contained no true alpha data; alpha |
|
1663 * is then inferred from the RGB values instead */ |
|
1664 |
|
1665 convertFromARGB(pixels, |
|
1666 img->width, |
|
1667 (img->internalFormat == M3G_A8 && !trueAlpha) ? M3G_L8 : img->internalFormat, |
|
1668 dst + line * stride); |
|
1669 |
|
1670 m3gUnmapObject(m3g, img->data); |
|
1671 m3gInvalidateImage(img); |
|
1672 } |
|
1673 } |
|
1674 |
|
1675 /*! |
|
1676 * \brief Sets a rectangular subregion of an image |
|
1677 * |
|
1678 * \param hImage Image object |
|
1679 * \param x x-coordinate in destination image |
|
1680 * \param y y-coordinate in destination image |
|
1681 * \param width width of source pixels |
|
1682 * \param height height of source pixels |
|
1683 * \param length length of source data, in bytes |
|
1684 * \param pixels source pixels |
|
1685 */ |
|
1686 M3G_API void m3gSetSubImage(M3GImage hImage, |
|
1687 M3Gint x, M3Gint y, |
|
1688 M3Gint width, M3Gint height, |
|
1689 M3Gint length, const void *pixels) |
|
1690 { |
|
1691 Interface *m3g; |
|
1692 Image *img = (Image *) hImage; |
|
1693 |
|
1694 M3GPixelFormat srcFormat; |
|
1695 M3Gsizei srcBpp; |
|
1696 |
|
1697 M3G_VALIDATE_OBJECT(img); |
|
1698 m3g = M3G_INTERFACE(img); |
|
1699 |
|
1700 /* Check for errors */ |
|
1701 |
|
1702 if (img->data == 0 || (img->flags & M3G_STATIC) != 0) { |
|
1703 M3G_ASSERT(!(img->flags & M3G_DYNAMIC)); |
|
1704 m3gRaiseError(m3g, M3G_INVALID_OPERATION); |
|
1705 return; |
|
1706 } |
|
1707 if (pixels == NULL) { |
|
1708 m3gRaiseError(m3g, M3G_INVALID_VALUE); |
|
1709 return; |
|
1710 } |
|
1711 if (x < 0 || y < 0 || width <= 0 || height <= 0 |
|
1712 || x+width > img->width || y+height > img->height) { |
|
1713 m3gRaiseError(m3g, M3G_INVALID_VALUE); |
|
1714 return; |
|
1715 } |
|
1716 |
|
1717 srcFormat = m3gInputDataFormat(img); |
|
1718 srcBpp = m3gBytesPerPixel(srcFormat); |
|
1719 |
|
1720 if (length < width * height * srcBpp) { |
|
1721 m3gRaiseError(m3g, M3G_INVALID_VALUE); |
|
1722 return; |
|
1723 } |
|
1724 |
|
1725 /* Copy the image data, doing a conversion if the input format |
|
1726 * does not match the internal storage format */ |
|
1727 { |
|
1728 const M3Gubyte *srcPixels = (const M3Gubyte*) pixels; |
|
1729 M3Gsizei srcStride = width * srcBpp; |
|
1730 |
|
1731 M3GPixelFormat dstFormat = img->internalFormat; |
|
1732 M3Gsizei dstBpp = m3gBytesPerPixel(dstFormat); |
|
1733 M3Gsizei dstStride = img->width * dstBpp; |
|
1734 M3Gubyte *dstPixels = |
|
1735 ((M3Gubyte *)m3gMapObject(m3g, img->data)) |
|
1736 + img->paletteBytes |
|
1737 + y * dstStride + x * dstBpp; |
|
1738 |
|
1739 M3Gint numLines = height, numPixels = width; |
|
1740 M3Gbool paletted = (img->flags & M3G_PALETTED) != 0; |
|
1741 |
|
1742 /* Optimize the copy for full image width */ |
|
1743 |
|
1744 if (width == img->width) { |
|
1745 numLines = 1; |
|
1746 numPixels = width * height; |
|
1747 } |
|
1748 |
|
1749 /* Copy a scanline at a time, converting as necessary */ |
|
1750 |
|
1751 while (numLines-- > 0) { |
|
1752 |
|
1753 /* Matching pixel formats are just copied without |
|
1754 * conversion, and all internally supported paletted |
|
1755 * formats match each other physically, so they can be |
|
1756 * copied as well */ |
|
1757 |
|
1758 if (dstFormat == srcFormat || img->paletteBytes > 0) { |
|
1759 m3gCopy(dstPixels, srcPixels, numPixels * dstBpp); |
|
1760 } |
|
1761 else { |
|
1762 if (!paletted) { |
|
1763 |
|
1764 /* Ordinary conversion into an internal format |
|
1765 * that is encoded differently from the external |
|
1766 * format; can not be a paletted image */ |
|
1767 |
|
1768 M3G_ASSERT((img->flags & M3G_PALETTED) == 0); |
|
1769 m3gConvertPixels(srcFormat, srcPixels, |
|
1770 dstFormat, dstPixels, |
|
1771 numPixels); |
|
1772 } |
|
1773 else { |
|
1774 M3G_ASSERT(!m3gSupportedPaletteFormat(img->format)); |
|
1775 |
|
1776 /* Palette indices for one-byte-per-pixel formats |
|
1777 * are just copied in and mapped to actual values |
|
1778 * later; multibyte paletted formats require a |
|
1779 * conversion into LA, RGB, or RGBA format |
|
1780 * intensity levels temporarily before remapping |
|
1781 * to actual colors in m3gSetImagePalette */ |
|
1782 |
|
1783 if (dstBpp == 1) { |
|
1784 m3gCopy(dstPixels, srcPixels, numPixels); |
|
1785 } |
|
1786 else { |
|
1787 m3gConvertPixels(M3G_L8, srcPixels, |
|
1788 dstFormat, dstPixels, |
|
1789 numPixels); |
|
1790 } |
|
1791 } |
|
1792 } |
|
1793 |
|
1794 srcPixels += srcStride; |
|
1795 dstPixels += dstStride; |
|
1796 } |
|
1797 |
|
1798 /* Release the image data and invalidate mipmap levels */ |
|
1799 |
|
1800 m3gUnmapObject(m3g, img->data); |
|
1801 m3gInvalidateImage(img); |
|
1802 } |
|
1803 M3G_VALIDATE_OBJECT(img); |
|
1804 } |
|
1805 |
|
1806 #undef SPAN_BUFFER_SIZE |
|
1807 |