|
1 /*------------------------------------------------------------------------ |
|
2 * |
|
3 * OpenVG 1.1 Reference Implementation |
|
4 * ----------------------------------- |
|
5 * |
|
6 * Copyright (c) 2007 The Khronos Group Inc. |
|
7 * Portions copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
8 * |
|
9 * Permission is hereby granted, free of charge, to any person obtaining a |
|
10 * copy of this software and /or associated documentation files |
|
11 * (the "Materials "), to deal in the Materials without restriction, |
|
12 * including without limitation the rights to use, copy, modify, merge, |
|
13 * publish, distribute, sublicense, and/or sell copies of the Materials, |
|
14 * and to permit persons to whom the Materials are furnished to do so, |
|
15 * subject to the following conditions: |
|
16 * |
|
17 * The above copyright notice and this permission notice shall be included |
|
18 * in all copies or substantial portions of the Materials. |
|
19 * |
|
20 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
23 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
|
24 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
25 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR |
|
26 * THE USE OR OTHER DEALINGS IN THE MATERIALS. |
|
27 * |
|
28 *//** |
|
29 * \file |
|
30 * \brief Implementation of Color and Image functions. |
|
31 * \note |
|
32 *//*-------------------------------------------------------------------*/ |
|
33 |
|
34 #include "riImage.h" |
|
35 #include "riRasterizer.h" |
|
36 #include "riContext.h" |
|
37 |
|
38 #ifndef __SFDYNAMICBLITTER_H |
|
39 # include "sfDynamicBlitter.h" |
|
40 #endif |
|
41 |
|
42 //============================================================================================== |
|
43 |
|
44 namespace OpenVGRI |
|
45 { |
|
46 |
|
47 /*-------------------------------------------------------------------*//*! |
|
48 * \brief Converts from numBits into a shifted mask |
|
49 * \param |
|
50 * \return |
|
51 * \note |
|
52 *//*-------------------------------------------------------------------*/ |
|
53 |
|
54 static RI_INLINE unsigned int bitsToMask(unsigned int bits, unsigned int shift) |
|
55 { |
|
56 return ((1<<bits)-1) << shift; |
|
57 } |
|
58 |
|
59 /*-------------------------------------------------------------------*//*! |
|
60 * \brief Converts from color (RIfloat) to an int with 1.0f mapped to the |
|
61 * given maximum with round-to-nearest semantics. |
|
62 * \param |
|
63 * \return |
|
64 * \note |
|
65 *//*-------------------------------------------------------------------*/ |
|
66 |
|
67 RI_INLINE int ffloor(RIfloat x) |
|
68 { |
|
69 return (x >= 0) ? (int)x : (int)(x-1); |
|
70 } |
|
71 |
|
72 //static const float FLOAT_0 = 0.0f; |
|
73 static const float FLOAT_0_5 = 0.5f; |
|
74 |
|
75 /* \note Rewrite this if time. */ |
|
76 static unsigned int colorToInt(RIfloat c, int maxc) |
|
77 { |
|
78 #if defined RI_USE_SSE |
|
79 /* |
|
80 Registers mapping: |
|
81 c <-> xmm0, |
|
82 maxc <-> xmm1 |
|
83 0 <-> xmm2 |
|
84 */ |
|
85 _asm |
|
86 { |
|
87 xorps xmm2, xmm2 ; xmm2 = 0 |
|
88 |
|
89 ;--------------------------------------------- |
|
90 ; Computing: xmm0 = (c * (RIfloat)maxc + 0.5f) |
|
91 ;--------------------------------------------- |
|
92 movss xmm0, dword ptr [c] ; xmm0 = c |
|
93 cvtsi2ss xmm1, dword ptr [maxc] ; xmm1 = (float)maxc |
|
94 mulss xmm0, xmm1 ; xmm0 = xmm0 * xmm1 = c * (float)maxc |
|
95 addss xmm0, FLOAT_0_5 ; xmm0 = xmm0 + 0.5f = c * (float)maxc + 0.5f |
|
96 |
|
97 ;--------------------------------------------- |
|
98 ; Computing: xmm0 = floor(xmm0) = floor(c * (RIfloat)maxc + 0.5f) |
|
99 ;--------------------------------------------- |
|
100 cvttss2si ebx, xmm0 ; ebx = (int)xmm0 |
|
101 mov eax, ebx ; eax = ebx = (int)xmm0 |
|
102 shr eax, 31 ; eax = sign(eax) = sign((int)xmm0) |
|
103 sub ebx, eax ; ebx = ebx - sign((int)xmm0) = (int)xmm0 - sign((int)xmm0) = (int)floor((int)xmm0) |
|
104 cvtsi2ss xmm0, ebx ; xmm0 = floor(xmm0) |
|
105 |
|
106 pmaxsw xmm0, xmm2; ; xmm0 = MAX(xmm0, 0) |
|
107 pminsw xmm0, xmm1 ; xmm0 = MIN(xmm0, maxc) |
|
108 cvttss2si eax, xmm0 ; return value = eax = (int)xmm0 |
|
109 } |
|
110 #else |
|
111 return RI_INT_MIN(RI_INT_MAX((int)ffloor(c * (RIfloat)maxc + 0.5f), 0), maxc); |
|
112 #endif |
|
113 } |
|
114 |
|
115 /*-------------------------------------------------------------------*//*! |
|
116 * \brief Converts from int to color (RIfloat) with the given maximum |
|
117 * mapped to 1.0f. |
|
118 * \param |
|
119 * \return |
|
120 * \note |
|
121 *//*-------------------------------------------------------------------*/ |
|
122 |
|
123 static RI_INLINE RIfloat intToColor(unsigned int i, unsigned int maxi) |
|
124 { |
|
125 return (RIfloat)(i & maxi) / (RIfloat)maxi; |
|
126 } |
|
127 |
|
128 void Color::Descriptor::toSmallDescriptor(Color::SmallDescriptor& smallDesc) const |
|
129 { |
|
130 switch (bitsPerPixel) |
|
131 { |
|
132 case 32: |
|
133 smallDesc.size = SIZE_32; |
|
134 break; |
|
135 case 24: |
|
136 smallDesc.size = SIZE_24; |
|
137 break; |
|
138 case 16: |
|
139 smallDesc.size = SIZE_16; |
|
140 break; |
|
141 case 8: |
|
142 smallDesc.size = SIZE_8; |
|
143 break; |
|
144 case 4: |
|
145 smallDesc.size = SIZE_4; |
|
146 break; |
|
147 default: |
|
148 RI_ASSERT(bitsPerPixel == 1); |
|
149 smallDesc.size = SIZE_1; |
|
150 break; |
|
151 } |
|
152 smallDesc.shape = shape; |
|
153 smallDesc.internalFormat = internalFormat; |
|
154 } |
|
155 |
|
156 Color::Descriptor Color::Descriptor::getDummyDescriptor() |
|
157 { |
|
158 static const Descriptor dummy = Color::Descriptor(8,0,8,8,8,16,8,24,0,0,sRGBA,32,SHAPE_ABGR); |
|
159 return dummy; |
|
160 } |
|
161 |
|
162 /** |
|
163 * \brief Determine the shape of the color format from other data. |
|
164 * \todo The naming is poor because it may be interpreted as returning the member |
|
165 * "shape". |
|
166 */ |
|
167 Color::Shape Color::Descriptor::getShape() const |
|
168 { |
|
169 // \todo There should be some easier way to define the shape so that it does |
|
170 // not need to be determined with so many conditions. |
|
171 |
|
172 if (isAlphaOnly()) |
|
173 { |
|
174 return SHAPE_A; |
|
175 } |
|
176 else if (isLuminance()) |
|
177 { |
|
178 if (alphaBits) |
|
179 { |
|
180 if (alphaShift == 0) |
|
181 return SHAPE_LA; |
|
182 return SHAPE_AL; |
|
183 } |
|
184 return SHAPE_L; |
|
185 } |
|
186 else if (!alphaBits) |
|
187 { |
|
188 if (bitsPerPixel == 32) |
|
189 { |
|
190 switch(redShift) |
|
191 { |
|
192 case 0: |
|
193 return SHAPE_XBGR; |
|
194 case 8: |
|
195 return SHAPE_BGRX; |
|
196 case 16: |
|
197 return SHAPE_XRGB; |
|
198 default: |
|
199 RI_ASSERT(redShift == 24); |
|
200 return SHAPE_RGBX; |
|
201 } |
|
202 } else if (bitsPerPixel == 24) |
|
203 { |
|
204 if (!redShift) |
|
205 return SHAPE_BGR; |
|
206 else |
|
207 { |
|
208 RI_ASSERT(redShift == 16); |
|
209 return SHAPE_RGB; |
|
210 } |
|
211 } else |
|
212 { |
|
213 RI_ASSERT(redBits == 5 && greenBits == 6 && blueBits == 5); |
|
214 if(redShift) |
|
215 return SHAPE_RGB; |
|
216 else |
|
217 return SHAPE_BGR; |
|
218 } |
|
219 } |
|
220 else |
|
221 { |
|
222 if (bitsPerPixel == 32) |
|
223 { |
|
224 switch(redShift) |
|
225 { |
|
226 case 0: |
|
227 return SHAPE_ABGR; |
|
228 case 8: |
|
229 return SHAPE_BGRA; |
|
230 case 16: |
|
231 return SHAPE_ARGB; |
|
232 default: |
|
233 RI_ASSERT(redShift == 24); |
|
234 return SHAPE_RGBA; |
|
235 } |
|
236 } else |
|
237 { |
|
238 RI_ASSERT(bitsPerPixel == 16); |
|
239 if (redBits == 5) |
|
240 { |
|
241 RI_ASSERT(greenBits == 5 && blueBits == 5 && alphaBits == 1); |
|
242 switch(redShift) |
|
243 { |
|
244 case 0: |
|
245 return SHAPE_ABGR; |
|
246 case 1: |
|
247 return SHAPE_BGRA; |
|
248 case 10: |
|
249 return SHAPE_ARGB; |
|
250 default: |
|
251 RI_ASSERT(redShift == 11); |
|
252 return SHAPE_RGBA; |
|
253 } |
|
254 } else |
|
255 { |
|
256 RI_ASSERT(redBits == 4 && greenBits == 4 && alphaBits == 4); |
|
257 switch(redShift) |
|
258 { |
|
259 case 0: |
|
260 return SHAPE_ABGR; |
|
261 case 4: |
|
262 return SHAPE_BGRA; |
|
263 case 8: |
|
264 return SHAPE_ARGB; |
|
265 default: |
|
266 RI_ASSERT(redShift == 12); |
|
267 return SHAPE_RGBA; |
|
268 } |
|
269 } |
|
270 } |
|
271 } |
|
272 } |
|
273 |
|
274 /*-------------------------------------------------------------------*//*! |
|
275 * \brief Converts from packed integer in a given format to a Color. |
|
276 * \param |
|
277 * \return |
|
278 * \note |
|
279 *//*-------------------------------------------------------------------*/ |
|
280 |
|
281 void Color::unpack(unsigned int inputData, const Color::Descriptor& inputDesc) |
|
282 { |
|
283 int rb = inputDesc.redBits; |
|
284 int gb = inputDesc.greenBits; |
|
285 int bb = inputDesc.blueBits; |
|
286 int ab = inputDesc.alphaBits; |
|
287 int lb = inputDesc.luminanceBits; |
|
288 int rs = inputDesc.redShift; |
|
289 int gs = inputDesc.greenShift; |
|
290 int bs = inputDesc.blueShift; |
|
291 int as = inputDesc.alphaShift; |
|
292 int ls = inputDesc.luminanceShift; |
|
293 |
|
294 m_format = inputDesc.internalFormat; |
|
295 if(lb) |
|
296 { //luminance |
|
297 r = g = b = intToColor(inputData >> ls, (1<<lb)-1); |
|
298 a = 1.0f; |
|
299 } |
|
300 else |
|
301 { //rgba |
|
302 r = rb ? intToColor(inputData >> rs, (1<<rb)-1) : (RIfloat)1.0f; |
|
303 g = gb ? intToColor(inputData >> gs, (1<<gb)-1) : (RIfloat)1.0f; |
|
304 b = bb ? intToColor(inputData >> bs, (1<<bb)-1) : (RIfloat)1.0f; |
|
305 a = ab ? intToColor(inputData >> as, (1<<ab)-1) : (RIfloat)1.0f; |
|
306 |
|
307 if(isPremultiplied()) |
|
308 { //clamp premultiplied color to alpha to enforce consistency |
|
309 r = RI_MIN(r, a); |
|
310 g = RI_MIN(g, a); |
|
311 b = RI_MIN(b, a); |
|
312 } |
|
313 } |
|
314 |
|
315 assertConsistency(); |
|
316 } |
|
317 |
|
318 /*-------------------------------------------------------------------*//*! |
|
319 * \brief Converts from Color to a packed integer in a given format. |
|
320 * \param |
|
321 * \return |
|
322 * \note |
|
323 *//*-------------------------------------------------------------------*/ |
|
324 |
|
325 unsigned int Color::pack(const Color::Descriptor& outputDesc) const |
|
326 { |
|
327 assertConsistency(); |
|
328 |
|
329 int rb = outputDesc.redBits; |
|
330 int gb = outputDesc.greenBits; |
|
331 int bb = outputDesc.blueBits; |
|
332 int ab = outputDesc.alphaBits; |
|
333 int lb = outputDesc.luminanceBits; |
|
334 int rs = outputDesc.redShift; |
|
335 int gs = outputDesc.greenShift; |
|
336 int bs = outputDesc.blueShift; |
|
337 int as = outputDesc.alphaShift; |
|
338 int ls = outputDesc.luminanceShift; |
|
339 |
|
340 if(lb) |
|
341 { //luminance |
|
342 RI_ASSERT(isLuminance()); |
|
343 return colorToInt(r, (1<<lb)-1) << ls; |
|
344 } |
|
345 else |
|
346 { //rgb |
|
347 RI_ASSERT(!isLuminance()); |
|
348 unsigned int cr = rb ? colorToInt(r, (1<<rb)-1) : 0; |
|
349 unsigned int cg = gb ? colorToInt(g, (1<<gb)-1) : 0; |
|
350 unsigned int cb = bb ? colorToInt(b, (1<<bb)-1) : 0; |
|
351 unsigned int ca = ab ? colorToInt(a, (1<<ab)-1) : 0; |
|
352 return packRGBAInteger(cr, rs, cg, gs, cb, bs, ca, as); |
|
353 } |
|
354 } |
|
355 |
|
356 /*-------------------------------------------------------------------*//*! |
|
357 * \brief Converts from the current internal format to another. |
|
358 * \param |
|
359 * \return |
|
360 * \note |
|
361 *//*-------------------------------------------------------------------*/ |
|
362 |
|
363 /* \todo Integer & lookup versions */ |
|
364 |
|
365 static RIfloat gamma(RIfloat c) |
|
366 { |
|
367 if( c <= 0.00304f ) |
|
368 c *= 12.92f; |
|
369 else |
|
370 c = 1.0556f * (RIfloat)pow(c, 1.0f/2.4f) - 0.0556f; |
|
371 return c; |
|
372 } |
|
373 |
|
374 static RIfloat invgamma(RIfloat c) |
|
375 { |
|
376 if( c <= 0.03928f ) |
|
377 c /= 12.92f; |
|
378 else |
|
379 c = (RIfloat)pow((c + 0.0556f)/1.0556f, 2.4f); |
|
380 return c; |
|
381 } |
|
382 |
|
383 static RIfloat lRGBtoL(RIfloat r, RIfloat g, RIfloat b) |
|
384 { |
|
385 return 0.2126f*r + 0.7152f*g + 0.0722f*b; |
|
386 } |
|
387 |
|
388 void Color::convert(InternalFormat outputFormat) |
|
389 { |
|
390 /* \todo This should probably be converted to integer code. */ |
|
391 |
|
392 assertConsistency(); |
|
393 |
|
394 if( m_format == outputFormat ) |
|
395 return; |
|
396 |
|
397 if(isPremultiplied()) |
|
398 { //unpremultiply |
|
399 RIfloat ooa = (a != 0.0f) ? 1.0f / a : (RIfloat)0.0f; |
|
400 r *= ooa; |
|
401 g *= ooa; |
|
402 b *= ooa; |
|
403 } |
|
404 |
|
405 //From Section 3.4.2 of OpenVG spec |
|
406 //1: sRGB = gamma(lRGB) |
|
407 //2: lRGB = invgamma(sRGB) |
|
408 //3: lL = 0.2126 lR + 0.7152 lG + 0.0722 lB |
|
409 //4: lRGB = lL |
|
410 //5: sL = gamma(lL) |
|
411 //6: lL = invgamma(sL) |
|
412 //7: sRGB = sL |
|
413 |
|
414 //Source/Dest lRGB sRGB lL sL |
|
415 //lRGB - 1 3 3,5 |
|
416 //sRGB 2 - 2,3 2,3,5 |
|
417 //lL 4 4,1 - 5 |
|
418 //sL 7,2 7 6 - |
|
419 |
|
420 const unsigned int shift = 3; |
|
421 unsigned int conversion = (m_format & (NONLINEAR | LUMINANCE)) | ((outputFormat & (NONLINEAR | LUMINANCE)) << shift); |
|
422 |
|
423 switch(conversion) |
|
424 { |
|
425 case lRGBA | (sRGBA << shift): r = gamma(r); g = gamma(g); b = gamma(b); break; //1 |
|
426 case lRGBA | (lLA << shift) : r = g = b = lRGBtoL(r, g, b); break; //3 |
|
427 case lRGBA | (sLA << shift) : r = g = b = gamma(lRGBtoL(r, g, b)); break; //3,5 |
|
428 case sRGBA | (lRGBA << shift): r = invgamma(r); g = invgamma(g); b = invgamma(b); break; //2 |
|
429 case sRGBA | (lLA << shift) : r = g = b = lRGBtoL(invgamma(r), invgamma(g), invgamma(b)); break; //2,3 |
|
430 case sRGBA | (sLA << shift) : r = g = b = gamma(lRGBtoL(invgamma(r), invgamma(g), invgamma(b))); break;//2,3,5 |
|
431 case lLA | (lRGBA << shift): break; //4 |
|
432 case lLA | (sRGBA << shift): r = g = b = gamma(r); break; //4,1 |
|
433 case lLA | (sLA << shift) : r = g = b = gamma(r); break; //5 |
|
434 case sLA | (lRGBA << shift): r = g = b = invgamma(r); break; //7,2 |
|
435 case sLA | (sRGBA << shift): break; //7 |
|
436 case sLA | (lLA << shift) : r = g = b = invgamma(r); break; //6 |
|
437 default: RI_ASSERT((m_format & (LUMINANCE | NONLINEAR)) == (outputFormat & (LUMINANCE | NONLINEAR))); break; //nop |
|
438 } |
|
439 |
|
440 if(outputFormat & PREMULTIPLIED) |
|
441 { //premultiply |
|
442 r *= a; |
|
443 g *= a; |
|
444 b *= a; |
|
445 } |
|
446 m_format = outputFormat; |
|
447 |
|
448 assertConsistency(); |
|
449 } |
|
450 |
|
451 /*------------------------------------------------------------------------*//*! |
|
452 * \brief Creates a pixel format descriptor out of VGImageFormat |
|
453 * \param |
|
454 * \return |
|
455 * \note Remove this function and use the "const" version for consistency. |
|
456 *//*------------------------------------------------------------------------*/ |
|
457 Color::Descriptor Color::formatToDescriptor(VGImageFormat format) |
|
458 { |
|
459 Descriptor desc; |
|
460 memset(&desc, 0, sizeof(Descriptor)); |
|
461 RI_ASSERT(isValidImageFormat(format)); |
|
462 |
|
463 int baseFormat = (int)format & 15; |
|
464 const int numBaseFormats = 15; |
|
465 RI_ASSERT(baseFormat >= 0 && baseFormat < numBaseFormats); |
|
466 int swizzleBits = ((int)format >> 6) & 3; |
|
467 |
|
468 /* base formats |
|
469 VG_sRGBX_8888 = 0, |
|
470 VG_sRGBA_8888 = 1, |
|
471 VG_sRGBA_8888_PRE = 2, |
|
472 VG_sRGB_565 = 3, |
|
473 VG_sRGBA_5551 = 4, |
|
474 VG_sRGBA_4444 = 5, |
|
475 VG_sL_8 = 6, |
|
476 VG_lRGBX_8888 = 7, |
|
477 VG_lRGBA_8888 = 8, |
|
478 VG_lRGBA_8888_PRE = 9, |
|
479 VG_lL_8 = 10, |
|
480 VG_A_8 = 11, |
|
481 VG_BW_1 = 12, |
|
482 VG_A_1 = 13, |
|
483 VG_A_4 = 14, |
|
484 */ |
|
485 |
|
486 static const int redBits[numBaseFormats] = {8, 8, 8, 5, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0}; |
|
487 static const int greenBits[numBaseFormats] = {8, 8, 8, 6, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0}; |
|
488 static const int blueBits[numBaseFormats] = {8, 8, 8, 5, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0}; |
|
489 static const int alphaBits[numBaseFormats] = {0, 8, 8, 0, 1, 4, 0, 0, 8, 8, 0, 8, 0, 1, 4}; |
|
490 static const int luminanceBits[numBaseFormats] = {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 1, 0, 0}; |
|
491 |
|
492 static const int redShifts[4*numBaseFormats] = {24, 24, 24, 11, 11, 12, 0, 24, 24, 24, 0, 0, 0, 0, 0, //RGBA |
|
493 16, 16, 16, 11, 10, 8, 0, 16, 16, 16, 0, 0, 0, 0, 0, //ARGB |
|
494 8, 8, 8, 0, 1, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0, //BGRA |
|
495 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //ABGR |
|
496 |
|
497 static const int greenShifts[4*numBaseFormats] = {16, 16, 16, 5, 6, 8, 0, 16, 16, 16, 0, 0, 0, 0, 0, //RGBA |
|
498 8, 8, 8, 5, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0, //ARGB |
|
499 16, 16, 16, 5, 6, 8, 0, 16, 16, 16, 0, 0, 0, 0, 0, //BGRA |
|
500 8, 8, 8, 5, 5, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0};//ABGR |
|
501 |
|
502 static const int blueShifts[4*numBaseFormats] = {8, 8, 8, 0, 1, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0, //RGBA |
|
503 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //ARGB |
|
504 24, 24, 24, 11, 11, 12, 0, 24, 24, 24, 0, 0, 0, 0, 0, //BGRA |
|
505 16, 16, 16, 11, 10, 8, 0, 16, 16, 16, 0, 0, 0, 0, 0};//ABGR |
|
506 |
|
507 static const int alphaShifts[4*numBaseFormats] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //RGBA |
|
508 0, 24, 24, 0, 15, 12, 0, 0, 24, 24, 0, 0, 0, 0, 0, //ARGB |
|
509 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //BGRA |
|
510 0, 24, 24, 0, 15, 12, 0, 0, 24, 24, 0, 0, 0, 0, 0};//ABGR |
|
511 |
|
512 static const int bpps[numBaseFormats] = {32, 32, 32, 16, 16, 16, 8, 32, 32, 32, 8, 8, 1, 1, 4}; |
|
513 |
|
514 static const InternalFormat internalFormats[numBaseFormats] = {sRGBA, sRGBA, sRGBA_PRE, sRGBA, sRGBA, sRGBA, sLA, lRGBA, lRGBA, lRGBA_PRE, lLA, lRGBA, lLA, lRGBA, lRGBA}; |
|
515 |
|
516 desc.redBits = redBits[baseFormat]; |
|
517 desc.greenBits = greenBits[baseFormat]; |
|
518 desc.blueBits = blueBits[baseFormat]; |
|
519 desc.alphaBits = alphaBits[baseFormat]; |
|
520 desc.luminanceBits = luminanceBits[baseFormat]; |
|
521 |
|
522 desc.redShift = redShifts[swizzleBits * numBaseFormats + baseFormat]; |
|
523 desc.greenShift = greenShifts[swizzleBits * numBaseFormats + baseFormat]; |
|
524 desc.blueShift = blueShifts[swizzleBits * numBaseFormats + baseFormat]; |
|
525 desc.alphaShift = alphaShifts[swizzleBits * numBaseFormats + baseFormat]; |
|
526 desc.luminanceShift = 0; //always zero |
|
527 |
|
528 desc.vgFormat = format; |
|
529 desc.bitsPerPixel = bpps[baseFormat]; |
|
530 desc.bytesPerPixel = desc.bitsPerPixel / 8; |
|
531 desc.internalFormat = internalFormats[baseFormat]; |
|
532 desc.shape = desc.getShape(); |
|
533 |
|
534 if (desc.alphaBits) |
|
535 { |
|
536 desc.maskBits = desc.alphaBits; |
|
537 desc.maskShift = desc.alphaShift; |
|
538 } |
|
539 else if (!desc.isLuminance()) |
|
540 { |
|
541 desc.maskBits = desc.redBits; |
|
542 desc.maskShift = desc.redShift; |
|
543 } |
|
544 else |
|
545 { |
|
546 desc.maskBits = desc.luminanceBits; |
|
547 desc.maskShift = desc.luminanceShift; |
|
548 } |
|
549 |
|
550 return desc; |
|
551 } |
|
552 |
|
553 |
|
554 struct DescToFormatMapping |
|
555 { |
|
556 Color::Descriptor desc; |
|
557 VGImageFormat format; |
|
558 }; |
|
559 |
|
560 RI_INLINE static bool isDescEqualToMapping(const Color::Descriptor& desc, const DescToFormatMapping &mapping) |
|
561 { |
|
562 if ((desc.redBits == mapping.desc.redBits) && |
|
563 (desc.redShift == mapping.desc.redShift) && |
|
564 (desc.greenBits == mapping.desc.greenBits) && |
|
565 (desc.greenShift == mapping.desc.greenShift) && |
|
566 (desc.blueBits == mapping.desc.blueBits) && |
|
567 (desc.blueShift == mapping.desc.blueShift) && |
|
568 (desc.alphaBits == mapping.desc.alphaBits) && |
|
569 (desc.alphaShift == mapping.desc.alphaShift) && |
|
570 (desc.luminanceBits == mapping.desc.luminanceBits) && |
|
571 (desc.luminanceShift == mapping.desc.luminanceShift) && |
|
572 (desc.internalFormat == mapping.desc.internalFormat) && |
|
573 (desc.bitsPerPixel == mapping.desc.bitsPerPixel)) |
|
574 return true; |
|
575 |
|
576 return false; |
|
577 } |
|
578 |
|
579 VGImageFormat Color::descriptorToVGImageFormat(const Descriptor& desc) |
|
580 { |
|
581 //Color::Descriptor::Descriptor(int dredBits, int dredShift, int dgreenBits, int dgreenShift, int dblueBits, int dblueShift, int dalphaBits, int dalphaShift, int dluminanceBits, int dluminanceShift, InternalFormat dinternalFormat, int dbpp) : |
|
582 // \todo These are hardcoded here only to allow constant initialization, they should be generated |
|
583 // using formatToDescriptor! |
|
584 static const DescToFormatMapping map[] = { |
|
585 /* RGB{A,X} channel ordering */ |
|
586 { formatToDescriptorConst(VG_sRGBX_8888), VG_sRGBX_8888 }, |
|
587 { formatToDescriptorConst(VG_sRGBA_8888), VG_sRGBA_8888 }, |
|
588 { formatToDescriptorConst(VG_sRGBA_8888_PRE), VG_sRGBA_8888_PRE }, |
|
589 { formatToDescriptorConst(VG_sRGB_565), VG_sRGB_565 }, |
|
590 { formatToDescriptorConst(VG_sRGBA_5551), VG_sRGBA_5551 }, |
|
591 { formatToDescriptorConst(VG_sRGBA_4444), VG_sRGBA_4444 }, |
|
592 { formatToDescriptorConst(VG_sL_8), VG_sL_8 }, |
|
593 { formatToDescriptorConst(VG_lRGBX_8888), VG_lRGBX_8888 }, |
|
594 { formatToDescriptorConst(VG_lRGBA_8888), VG_lRGBA_8888 }, |
|
595 { formatToDescriptorConst(VG_lRGBA_8888_PRE), VG_lRGBA_8888_PRE }, |
|
596 { formatToDescriptorConst(VG_lL_8), VG_lL_8 }, |
|
597 { formatToDescriptorConst(VG_A_8), VG_A_8 }, |
|
598 { formatToDescriptorConst(VG_BW_1), VG_BW_1 }, |
|
599 { formatToDescriptorConst(VG_A_1), VG_A_1 }, |
|
600 { formatToDescriptorConst(VG_A_4), VG_A_4 }, |
|
601 |
|
602 /* {A,X}RGB channel ordering */ |
|
603 { formatToDescriptorConst(VG_sXRGB_8888), VG_sXRGB_8888 }, |
|
604 { formatToDescriptorConst(VG_sARGB_8888), VG_sARGB_8888 }, |
|
605 { formatToDescriptorConst(VG_sARGB_8888_PRE), VG_sARGB_8888_PRE }, |
|
606 { formatToDescriptorConst(VG_sARGB_1555), VG_sARGB_1555 }, |
|
607 { formatToDescriptorConst(VG_sARGB_4444), VG_sARGB_4444 }, |
|
608 { formatToDescriptorConst(VG_lXRGB_8888), VG_lXRGB_8888 }, |
|
609 { formatToDescriptorConst(VG_lARGB_8888), VG_lARGB_8888 }, |
|
610 { formatToDescriptorConst(VG_lARGB_8888_PRE), VG_lARGB_8888_PRE }, |
|
611 |
|
612 /* BGR{A,X} channel ordering */ |
|
613 { formatToDescriptorConst(VG_sBGRX_8888), VG_sBGRX_8888 }, |
|
614 { formatToDescriptorConst(VG_sBGRA_8888), VG_sBGRA_8888 }, |
|
615 { formatToDescriptorConst(VG_sBGRA_8888_PRE), VG_sBGRA_8888_PRE }, |
|
616 { formatToDescriptorConst(VG_sBGR_565), VG_sBGR_565 }, |
|
617 { formatToDescriptorConst(VG_sBGRA_5551), VG_sBGRA_5551 }, |
|
618 { formatToDescriptorConst(VG_sBGRA_4444), VG_sBGRA_4444 }, |
|
619 { formatToDescriptorConst(VG_lBGRX_8888), VG_lBGRX_8888 }, |
|
620 { formatToDescriptorConst(VG_lBGRA_8888), VG_lBGRA_8888 }, |
|
621 { formatToDescriptorConst(VG_lBGRA_8888_PRE), VG_lBGRA_8888_PRE }, |
|
622 |
|
623 /* {A,X}BGR channel ordering */ |
|
624 { formatToDescriptorConst(VG_sXBGR_8888), VG_sXBGR_8888 }, |
|
625 { formatToDescriptorConst(VG_sABGR_8888), VG_sABGR_8888 }, |
|
626 { formatToDescriptorConst(VG_sABGR_8888_PRE), VG_sABGR_8888_PRE }, |
|
627 { formatToDescriptorConst(VG_sABGR_1555), VG_sABGR_1555 }, |
|
628 { formatToDescriptorConst(VG_sABGR_4444), VG_sABGR_4444 }, |
|
629 { formatToDescriptorConst(VG_lXBGR_8888), VG_lXBGR_8888 }, |
|
630 { formatToDescriptorConst(VG_lABGR_8888), VG_lABGR_8888 }, |
|
631 { formatToDescriptorConst(VG_lABGR_8888_PRE), VG_lABGR_8888_PRE }, |
|
632 }; |
|
633 |
|
634 for (size_t i = 0; i < sizeof(map)/sizeof(map[0]); i++) |
|
635 { |
|
636 if (isDescEqualToMapping(desc, map[i])) |
|
637 return map[i].format; |
|
638 } |
|
639 RI_ASSERT(false); |
|
640 return (VGImageFormat)-1; |
|
641 } |
|
642 |
|
643 /*-------------------------------------------------------------------*//*! |
|
644 * \brief Checks if the pixel format descriptor is valid (i.e. all the |
|
645 * values are supported by the RI) |
|
646 * \param |
|
647 * \return |
|
648 * \note |
|
649 *//*-------------------------------------------------------------------*/ |
|
650 |
|
651 bool Color::isValidDescriptor(const Color::Descriptor& desc) |
|
652 { |
|
653 //A valid descriptor has 1, 2, 4, 8, 16, or 32 bits per pixel, and either luminance or rgba channels, but not both. |
|
654 //Any of the rgba channels can be missing, and not all bits need to be used. Maximum channel bit depth is 8. |
|
655 int rb = desc.redBits; |
|
656 int gb = desc.greenBits; |
|
657 int bb = desc.blueBits; |
|
658 int ab = desc.alphaBits; |
|
659 int lb = desc.luminanceBits; |
|
660 int rs = desc.redShift; |
|
661 int gs = desc.greenShift; |
|
662 int bs = desc.blueShift; |
|
663 int as = desc.alphaShift; |
|
664 int ls = desc.luminanceShift; |
|
665 int bpp = desc.bitsPerPixel; |
|
666 |
|
667 int rgbaBits = rb + gb + bb + ab; |
|
668 if(rb < 0 || rb > 8 || rs < 0 || rs + rb > bpp || !(rb || !rs)) |
|
669 return false; //invalid channel description |
|
670 if(gb < 0 || gb > 8 || gs < 0 || gs + gb > bpp || !(gb || !gs)) |
|
671 return false; //invalid channel description |
|
672 if(bb < 0 || bb > 8 || bs < 0 || bs + bb > bpp || !(bb || !bs)) |
|
673 return false; //invalid channel description |
|
674 if(ab < 0 || ab > 8 || as < 0 || as + ab > bpp || !(ab || !as)) |
|
675 return false; //invalid channel description |
|
676 if(lb < 0 || lb > 8 || ls < 0 || ls + lb > bpp || !(lb || !ls)) |
|
677 return false; //invalid channel description |
|
678 |
|
679 #if 0 |
|
680 if(rgbaBits && lb) |
|
681 return false; //can't have both rgba and luminance |
|
682 #endif |
|
683 if(!rgbaBits && !lb) |
|
684 return false; //must have either rgba or luminance |
|
685 if(rgbaBits) |
|
686 { //rgba |
|
687 if(rb+gb+bb == 0) |
|
688 { //alpha only |
|
689 if(rs || gs || bs || as || ls) |
|
690 return false; //wrong shifts (even alpha shift must be zero) |
|
691 if((ab != 1 && ab != 2 && ab != 4 && ab != 8) || bpp != ab) |
|
692 return false; //alpha size must be 1, 2, 4, or, 8, bpp must match |
|
693 } |
|
694 else |
|
695 { //rgba |
|
696 if(rgbaBits > bpp) |
|
697 return false; //bpp must be greater than or equal to the sum of rgba bits |
|
698 if(!(bpp == 32 || bpp == 16 || bpp == 8)) |
|
699 return false; //only 1, 2, and 4 byte formats are supported for rgba |
|
700 |
|
701 unsigned int rm = bitsToMask((unsigned int)rb, (unsigned int)rs); |
|
702 unsigned int gm = bitsToMask((unsigned int)gb, (unsigned int)gs); |
|
703 unsigned int bm = bitsToMask((unsigned int)bb, (unsigned int)bs); |
|
704 unsigned int am = bitsToMask((unsigned int)ab, (unsigned int)as); |
|
705 if((rm & gm) || (rm & bm) || (rm & am) || (gm & bm) || (gm & am) || (bm & am)) |
|
706 return false; //channels overlap |
|
707 } |
|
708 } |
|
709 else |
|
710 { //luminance |
|
711 if(rs || gs || bs || as || ls) |
|
712 return false; //wrong shifts (even luminance shift must be zero) |
|
713 if(!(lb == 1 || lb == 8) || bpp != lb) |
|
714 return false; //luminance size must be either 1 or 8, bpp must match |
|
715 } |
|
716 |
|
717 if(desc.vgFormat != -1) |
|
718 { |
|
719 if(!isValidImageFormat(desc.vgFormat)) |
|
720 return false; //invalid image format |
|
721 |
|
722 Descriptor d = formatToDescriptor(desc.vgFormat); |
|
723 if(d.redBits != rb || d.greenBits != gb || d.blueBits != bb || d.alphaBits != ab || d.luminanceBits != lb || |
|
724 d.redShift != rs || d.greenShift != gs || d.blueShift != bs || d.alphaShift != as || d.luminanceShift != ls || |
|
725 d.bitsPerPixel != bpp) |
|
726 return false; //if the descriptor has a VGImageFormat, it must match the bits, shifts, and bpp |
|
727 } |
|
728 |
|
729 if((unsigned int)desc.internalFormat & ~(Color::PREMULTIPLIED | Color::NONLINEAR | Color::LUMINANCE)) |
|
730 return false; //invalid internal format |
|
731 |
|
732 return true; |
|
733 } |
|
734 |
|
735 //============================================================================================== |
|
736 |
|
737 //============================================================================================== |
|
738 |
|
739 IntegerColor::IntegerColor(const Color& color) |
|
740 { |
|
741 r = (RIuint32)(color.r * 255.0f + 0.5f); |
|
742 g = (RIuint32)(color.g * 255.0f + 0.5f); |
|
743 b = (RIuint32)(color.b * 255.0f + 0.5f); |
|
744 a = (RIuint32)(color.a * 255.0f + 0.5f); |
|
745 } |
|
746 |
|
747 //============================================================================================== |
|
748 |
|
749 //============================================================================================== |
|
750 |
|
751 /*-------------------------------------------------------------------*//*! |
|
752 * \brief Constructs a blank image. |
|
753 * \param |
|
754 * \return |
|
755 * \note |
|
756 *//*-------------------------------------------------------------------*/ |
|
757 |
|
758 Image::Image(const Color::Descriptor& desc, int width, int height, VGbitfield allowedQuality) : |
|
759 m_desc(desc), |
|
760 m_width(width), |
|
761 m_height(height), |
|
762 m_allowedQuality(allowedQuality), |
|
763 m_inUse(0), |
|
764 m_stride(0), |
|
765 m_data(NULL), |
|
766 m_referenceCount(0), |
|
767 m_ownsData(true), |
|
768 m_parent(NULL), |
|
769 m_storageOffsetX(0), |
|
770 m_storageOffsetY(0), |
|
771 m_unsafeData(false) |
|
772 { |
|
773 RI_ASSERT(Color::isValidDescriptor(m_desc)); |
|
774 RI_ASSERT(width > 0 && height > 0); |
|
775 |
|
776 m_stride = (m_width*m_desc.bitsPerPixel+7)/8; |
|
777 |
|
778 m_data = RI_NEW_ARRAY(RIuint8, m_stride*m_height); //throws bad_alloc |
|
779 memset(m_data, 0, m_stride*m_height); //clear image |
|
780 } |
|
781 |
|
782 /*-------------------------------------------------------------------*//*! |
|
783 * \brief Constructs an image that uses an external array for its data |
|
784 * storage. |
|
785 * \param |
|
786 * \return |
|
787 * \note This is meant for internal use to make blitting easier |
|
788 * \note Now this is "tagged" into m_unsafeData if necessary. |
|
789 * Using this constructor may then affect performance. |
|
790 *//*-------------------------------------------------------------------*/ |
|
791 |
|
792 Image::Image(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data) : |
|
793 m_desc(desc), |
|
794 m_width(width), |
|
795 m_height(height), |
|
796 m_allowedQuality(0), |
|
797 m_inUse(0), |
|
798 m_stride(stride), |
|
799 m_data(data), |
|
800 m_referenceCount(0), |
|
801 m_ownsData(false), |
|
802 m_parent(NULL), |
|
803 m_storageOffsetX(0), |
|
804 m_storageOffsetY(0), |
|
805 m_unsafeData(false) |
|
806 { |
|
807 RI_ASSERT(Color::isValidDescriptor(m_desc)); |
|
808 RI_ASSERT(width > 0 && height > 0); |
|
809 RI_ASSERT(data); |
|
810 setUnsafe(true); // External data always potentially unsafe, see note above. |
|
811 } |
|
812 |
|
813 /*-------------------------------------------------------------------*//*! |
|
814 * \brief Construcs a child image. |
|
815 * \param |
|
816 * \return |
|
817 * \note |
|
818 *//*-------------------------------------------------------------------*/ |
|
819 |
|
820 Image::Image(Image* parent, int x, int y, int width, int height) : |
|
821 m_desc(Color::formatToDescriptor(VG_sRGBA_8888)), //dummy initialization, will be overwritten below (can't read from parent->m_desc before knowing the pointer is valid) |
|
822 m_width(width), |
|
823 m_height(height), |
|
824 m_allowedQuality(0), |
|
825 m_inUse(0), |
|
826 m_stride(0), |
|
827 m_data(NULL), |
|
828 m_referenceCount(0), |
|
829 m_ownsData(false), |
|
830 m_parent(parent), |
|
831 m_storageOffsetX(0), |
|
832 m_storageOffsetY(0), |
|
833 m_unsafeData(false) |
|
834 { |
|
835 RI_ASSERT(parent); |
|
836 RI_ASSERT(x >= 0 && y >= 0 && width > 0 && height > 0); |
|
837 RI_ASSERT(RI_INT_ADDSATURATE(x,width) <= parent->m_width && RI_INT_ADDSATURATE(y,height) <= parent->m_height); //child image must be contained in parent |
|
838 |
|
839 m_desc = parent->m_desc; |
|
840 RI_ASSERT(Color::isValidDescriptor(m_desc)); |
|
841 m_allowedQuality = parent->m_allowedQuality; |
|
842 m_stride = parent->m_stride; |
|
843 m_data = parent->m_data; |
|
844 m_storageOffsetX = parent->m_storageOffsetX + x; |
|
845 m_storageOffsetY = parent->m_storageOffsetY + y; |
|
846 |
|
847 //increase the reference and use count of the parent |
|
848 addInUse(); |
|
849 parent->addInUse(); |
|
850 parent->addReference(); |
|
851 m_unsafeData = parent->m_unsafeData; |
|
852 } |
|
853 |
|
854 /*-------------------------------------------------------------------*//*! |
|
855 * \brief Image destructor. |
|
856 * \param |
|
857 * \return |
|
858 * \note |
|
859 *//*-------------------------------------------------------------------*/ |
|
860 |
|
861 Image::~Image() |
|
862 { |
|
863 RI_ASSERT(m_referenceCount == 0); |
|
864 |
|
865 if(m_parent) |
|
866 { |
|
867 //decrease the reference and use count of the parent |
|
868 removeInUse(); |
|
869 m_parent->removeInUse(); |
|
870 if(!m_parent->removeReference()) |
|
871 RI_DELETE(m_parent); |
|
872 } |
|
873 RI_ASSERT(m_inUse == 0); |
|
874 |
|
875 |
|
876 if(m_ownsData) |
|
877 { |
|
878 RI_ASSERT(!m_parent); //can't have parent if owns the data |
|
879 RI_DELETE_ARRAY(m_data); //delete image data if we own it |
|
880 } |
|
881 } |
|
882 |
|
883 /*-------------------------------------------------------------------*//*! |
|
884 * \brief Returns true if the two images share pixels. |
|
885 * \param |
|
886 * \return |
|
887 * \note |
|
888 *//*-------------------------------------------------------------------*/ |
|
889 |
|
890 bool Image::overlaps(const Image* src) const |
|
891 { |
|
892 RI_ASSERT(src); |
|
893 |
|
894 if(m_data != src->m_data) |
|
895 return false; //images don't share data |
|
896 |
|
897 //check if the image storage regions overlap |
|
898 Rectangle r(m_storageOffsetX, m_storageOffsetY, m_width, m_height); |
|
899 r.intersect(Rectangle(src->m_storageOffsetX, src->m_storageOffsetY, src->m_width, src->m_height)); |
|
900 if(!r.width || !r.height) |
|
901 return false; //intersection is empty, images don't overlap |
|
902 |
|
903 return true; |
|
904 } |
|
905 |
|
906 /** |
|
907 * \brief Expand log2 bpp packed pixel (single value) to 8 bits. This will |
|
908 * Result in 8, 4, or 2 same pixel values to be packed into the return value. |
|
909 */ |
|
910 RI_INLINE static RIuint32 logExpand8(RIuint32 packedColor, int srcBits) |
|
911 { |
|
912 RI_ASSERT(srcBits == 4 || srcBits == 2 || srcBits == 1); |
|
913 RIuint32 ret = packedColor; |
|
914 int n = srcBits; |
|
915 while (n < 8) |
|
916 { |
|
917 ret |= ret << n; |
|
918 n += n; |
|
919 } |
|
920 return ret; |
|
921 } |
|
922 |
|
923 RI_INLINE void Image::fillPacked(RIuint32 packedColor) |
|
924 { |
|
925 RIuint32 pc = packedColor; |
|
926 int Bpp = m_desc.bitsPerPixel / 8; |
|
927 int nSetsPerScanline = m_width; |
|
928 |
|
929 RI_ASSERT(nSetsPerScanline); |
|
930 // \todo 1bpp and 4bpp mask formats must be supported. fillPackedPixels should |
|
931 // automatically work, but riMemSet32 path needs a bit more logic. |
|
932 // \note < 8bpp formats are always rounded to 8-bit boundaries at scanline end. |
|
933 // It is assumed that the "padding bits" may be filled. |
|
934 |
|
935 if (m_desc.bitsPerPixel < 8) |
|
936 { |
|
937 pc = logExpand8(packedColor, m_desc.bitsPerPixel); |
|
938 Bpp = 1; |
|
939 nSetsPerScanline = (m_width * m_desc.bitsPerPixel + 7) / 8; |
|
940 //nSetsPerScanline /= (8/m_desc.bitsPerPixel); |
|
941 } |
|
942 |
|
943 RI_ASSERT(Bpp <= 4 && Bpp >= 1); |
|
944 |
|
945 if (m_stride == ((m_desc.bitsPerPixel*m_width+7)/8)) |
|
946 { |
|
947 const int nPixels = nSetsPerScanline * m_height; |
|
948 riMemSet32(m_data, pc, nPixels, Bpp); |
|
949 } else |
|
950 { |
|
951 RIuint8 *ptr = (RIuint8*)m_data; |
|
952 // set per-scanline |
|
953 for (int y = 0; y < m_height; y++) |
|
954 { |
|
955 riMemSet32(ptr, pc, nSetsPerScanline, Bpp); |
|
956 ptr += m_stride; |
|
957 } |
|
958 } |
|
959 } |
|
960 |
|
961 /*-------------------------------------------------------------------*//*! |
|
962 * \brief Clears a rectangular portion of an image with the given clear color. |
|
963 * \param |
|
964 * \return |
|
965 * \note |
|
966 *//*-------------------------------------------------------------------*/ |
|
967 |
|
968 void Image::clear(const Color& clearColor, int x, int y, int w, int h) |
|
969 { |
|
970 RI_ASSERT(m_data); |
|
971 RI_ASSERT(m_referenceCount > 0); |
|
972 |
|
973 |
|
974 //intersect clear region with image bounds |
|
975 Rectangle r(0,0,m_width,m_height); |
|
976 r.intersect(Rectangle(x,y,w,h)); |
|
977 if(!r.width || !r.height) |
|
978 return; //intersection is empty or one of the rectangles is invalid |
|
979 |
|
980 Color col = clearColor; |
|
981 col.clamp(); |
|
982 col.convert(getDescriptor().internalFormat); |
|
983 |
|
984 IntegerColor ic = IntegerColor(col); |
|
985 ic.truncateColor(getDescriptor()); |
|
986 const RIuint32 c = ic.getPackedColor(getDescriptor()); |
|
987 |
|
988 if (r.width == getWidth() && r.height == getHeight() && !m_parent) |
|
989 fillPacked(c); |
|
990 else |
|
991 { |
|
992 fillPackedRectangle(r.x, r.y, r.width, r.height, c); |
|
993 } |
|
994 } |
|
995 |
|
996 #if 0 |
|
997 static RIfloat ditherChannel(RIfloat c, int bits, RIfloat m) |
|
998 { |
|
999 RIfloat fc = c * (RIfloat)((1<<bits)-1); |
|
1000 RIfloat ic = (RIfloat)floor(fc); |
|
1001 if(fc - ic > m) ic += 1.0f; |
|
1002 return RI_MIN(ic / (RIfloat)((1<<bits)-1), 1.0f); |
|
1003 } |
|
1004 #endif |
|
1005 |
|
1006 static void computeBlitRegion(int& sx, int& sy, int& dx, int& dy, int& w, int& h, int srcWidth, int srcHeight, int dstWidth, int dstHeight) |
|
1007 { |
|
1008 RI_ASSERT(w > 0 && h > 0); |
|
1009 sx = RI_INT_MIN(RI_INT_MAX(sx, (int)(RI_INT32_MIN>>2)), (int)(RI_INT32_MAX>>2)); |
|
1010 sy = RI_INT_MIN(RI_INT_MAX(sy, (int)(RI_INT32_MIN>>2)), (int)(RI_INT32_MAX>>2)); |
|
1011 dx = RI_INT_MIN(RI_INT_MAX(dx, (int)(RI_INT32_MIN>>2)), (int)(RI_INT32_MAX>>2)); |
|
1012 dy = RI_INT_MIN(RI_INT_MAX(dy, (int)(RI_INT32_MIN>>2)), (int)(RI_INT32_MAX>>2)); |
|
1013 w = RI_INT_MIN(w, (int)(RI_INT32_MAX>>2)); |
|
1014 h = RI_INT_MIN(h, (int)(RI_INT32_MAX>>2)); |
|
1015 int srcsx = sx, srcex = sx + w, dstsx = dx, dstex = dx + w; |
|
1016 if(srcsx < 0) |
|
1017 { |
|
1018 dstsx -= srcsx; |
|
1019 srcsx = 0; |
|
1020 } |
|
1021 if(srcex > srcWidth) |
|
1022 { |
|
1023 dstex -= srcex - srcWidth; |
|
1024 srcex = srcWidth; |
|
1025 } |
|
1026 if(dstsx < 0) |
|
1027 { |
|
1028 srcsx -= dstsx; |
|
1029 dstsx = 0; |
|
1030 } |
|
1031 if(dstex > dstWidth) |
|
1032 { |
|
1033 srcex -= dstex - dstWidth; |
|
1034 dstex = dstWidth; |
|
1035 } |
|
1036 RI_ASSERT(srcsx >= 0 && dstsx >= 0 && srcex <= srcWidth && dstex <= dstWidth); |
|
1037 w = srcex - srcsx; |
|
1038 RI_ASSERT(w == dstex - dstsx); |
|
1039 |
|
1040 int srcsy = sy, srcey = sy + h, dstsy = dy, dstey = dy + h; |
|
1041 if(srcsy < 0) |
|
1042 { |
|
1043 dstsy -= srcsy; |
|
1044 srcsy = 0; |
|
1045 } |
|
1046 if(srcey > srcHeight) |
|
1047 { |
|
1048 dstey -= srcey - srcHeight; |
|
1049 srcey = srcHeight; |
|
1050 } |
|
1051 if(dstsy < 0) |
|
1052 { |
|
1053 srcsy -= dstsy; |
|
1054 dstsy = 0; |
|
1055 } |
|
1056 if(dstey > dstHeight) |
|
1057 { |
|
1058 srcey -= dstey - dstHeight; |
|
1059 dstey = dstHeight; |
|
1060 } |
|
1061 RI_ASSERT(srcsy >= 0 && dstsy >= 0 && srcey <= srcHeight && dstey <= dstHeight); |
|
1062 h = srcey - srcsy; |
|
1063 RI_ASSERT(h == dstey - dstsy); |
|
1064 sx = srcsx; |
|
1065 sy = srcsy; |
|
1066 dx = dstsx; |
|
1067 dy = dstsy; |
|
1068 } |
|
1069 |
|
1070 /*-------------------------------------------------------------------*//*! |
|
1071 * \brief Blits a source region to destination. Source and destination |
|
1072 * can overlap. |
|
1073 * \param |
|
1074 * \return |
|
1075 * \note |
|
1076 *//*-------------------------------------------------------------------*/ |
|
1077 |
|
1078 // \todo Extract dithering kernel and put it into the blitter |
|
1079 #if 0 |
|
1080 void Image::blit(VGContext* context, const Image* src, int sx, int sy, int dx, int dy, int w, int h, bool dither) |
|
1081 { |
|
1082 //img=>img: vgCopyImage |
|
1083 //img=>user: vgGetImageSubData |
|
1084 //user=>img: vgImageSubData |
|
1085 |
|
1086 // \todo Implement dither to blitter. |
|
1087 this->blit(context, src, sx, sy, dx, dy, w, h, NULL, dither); |
|
1088 RI_ASSERT(src.m_data); //source exists |
|
1089 RI_ASSERT(m_data); //destination exists |
|
1090 RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); |
|
1091 |
|
1092 computeBlitRegion(sx, sy, dx, dy, w, h, src.m_width, src.m_height, m_width, m_height); |
|
1093 if(w <= 0 || h <= 0) |
|
1094 return; //zero area |
|
1095 |
|
1096 Array<Color> tmp; |
|
1097 tmp.resize(w*h); //throws bad_alloc |
|
1098 |
|
1099 //copy source region to tmp |
|
1100 for(int j=0;j<h;j++) |
|
1101 { |
|
1102 for(int i=0;i<w;i++) |
|
1103 { |
|
1104 Color c = src.readPixel(sx + i, sy + j); |
|
1105 c.convert(m_desc.internalFormat); |
|
1106 tmp[j*w+i] = c; |
|
1107 } |
|
1108 } |
|
1109 |
|
1110 int rbits = m_desc.redBits, gbits = m_desc.greenBits, bbits = m_desc.blueBits, abits = m_desc.alphaBits; |
|
1111 if(m_desc.isLuminance()) |
|
1112 { |
|
1113 rbits = gbits = bbits = m_desc.luminanceBits; |
|
1114 abits = 0; |
|
1115 } |
|
1116 |
|
1117 //write tmp to destination region |
|
1118 for(int j=0;j<h;j++) |
|
1119 { |
|
1120 for(int i=0;i<w;i++) |
|
1121 { |
|
1122 Color col = tmp[j*w+i]; |
|
1123 |
|
1124 if(dither) |
|
1125 { |
|
1126 static const int matrix[16] = { |
|
1127 0, 8, 2, 10, |
|
1128 12, 4, 14, 6, |
|
1129 3, 11, 1, 9, |
|
1130 15, 7, 13, 5}; |
|
1131 int x = i & 3; |
|
1132 int y = j & 3; |
|
1133 RIfloat m = matrix[y*4+x] / 16.0f; |
|
1134 |
|
1135 if(rbits) col.r = ditherChannel(col.r, rbits, m); |
|
1136 if(gbits) col.g = ditherChannel(col.g, gbits, m); |
|
1137 if(bbits) col.b = ditherChannel(col.b, bbits, m); |
|
1138 if(abits) col.a = ditherChannel(col.a, abits, m); |
|
1139 } |
|
1140 |
|
1141 writePixel(dx + i, dy + j, col); |
|
1142 } |
|
1143 } |
|
1144 } |
|
1145 #endif |
|
1146 |
|
1147 /** |
|
1148 * \brief Common body for drawImage-functions (one is the actual drawImage, and the |
|
1149 * other one is used for scissored image-set operations. |
|
1150 * \todo Reorganize all image draw operations to use this function. |
|
1151 */ |
|
1152 static bool drawImageBody(VGContext* context, Image* image, const Matrix3x3& userToSurfaceMatrix, |
|
1153 VGImageQuality imageQuality, |
|
1154 VGBlendMode blendMode, |
|
1155 bool hasMasking, |
|
1156 bool hasColorTransform, |
|
1157 VGImageMode imageMode) |
|
1158 { |
|
1159 Drawable* drawable = context->getCurrentDrawable(); |
|
1160 if(!drawable) |
|
1161 return false; //no EGL surface is current at the moment |
|
1162 |
|
1163 Image* img = (Image*)image; |
|
1164 //transform image corners into the surface space |
|
1165 Vector3 p0(0, 0, 1); |
|
1166 Vector3 p1(0, (RIfloat)img->getHeight(), 1); |
|
1167 Vector3 p2((RIfloat)img->getWidth(), (RIfloat)img->getHeight(), 1); |
|
1168 Vector3 p3((RIfloat)img->getWidth(), 0, 1); |
|
1169 |
|
1170 p0 = userToSurfaceMatrix * p0; |
|
1171 p1 = userToSurfaceMatrix * p1; |
|
1172 p2 = userToSurfaceMatrix * p2; |
|
1173 p3 = userToSurfaceMatrix * p3; |
|
1174 if(p0.z <= 0.0f || p1.z <= 0.0f || p2.z <= 0.0f || p3.z <= 0.0f) |
|
1175 return false; |
|
1176 |
|
1177 //projection |
|
1178 p0 *= 1.0f/p0.z; |
|
1179 p1 *= 1.0f/p1.z; |
|
1180 p2 *= 1.0f/p2.z; |
|
1181 p3 *= 1.0f/p3.z; |
|
1182 |
|
1183 Rasterizer& rasterizer = context->m_rasterizer; |
|
1184 rasterizer.clear(); |
|
1185 |
|
1186 if(context->m_scissoring) |
|
1187 rasterizer.setScissor(context->m_scissor); //throws bad_alloc |
|
1188 |
|
1189 PixelPipe& pixelPipe = context->m_pixelPipe; |
|
1190 pixelPipe.setTileFillColor(context->m_tileFillColor); |
|
1191 pixelPipe.setPaint((Paint*)context->m_fillPaint); |
|
1192 const bool aa = imageQuality == VG_IMAGE_QUALITY_NONANTIALIASED ? false : true; |
|
1193 rasterizer.setAntiAliasing(aa); |
|
1194 pixelPipe.setImageQuality(imageQuality); |
|
1195 pixelPipe.setBlendMode(blendMode); |
|
1196 pixelPipe.setRenderToMask(false); |
|
1197 pixelPipe.setDrawable(drawable); |
|
1198 pixelPipe.setMask(hasMasking); |
|
1199 pixelPipe.setColorTransform(hasColorTransform, context->m_colorTransformValues); |
|
1200 |
|
1201 Matrix3x3 surfaceToImageMatrix = userToSurfaceMatrix; |
|
1202 Matrix3x3 surfaceToPaintMatrix = userToSurfaceMatrix * context->m_fillPaintToUser; |
|
1203 if(surfaceToImageMatrix.invert() && surfaceToPaintMatrix.invert()) |
|
1204 { |
|
1205 VGImageMode imode = imageMode; |
|
1206 |
|
1207 if(!surfaceToPaintMatrix.isAffine()) |
|
1208 imode = VG_DRAW_IMAGE_NORMAL; //if paint matrix is not affine, always use normal image mode |
|
1209 |
|
1210 surfaceToPaintMatrix[2].set(0,0,1); //force affine |
|
1211 |
|
1212 pixelPipe.setImage(img, imode); |
|
1213 pixelPipe.setSurfaceToPaintMatrix(surfaceToPaintMatrix); |
|
1214 pixelPipe.setSurfaceToImageMatrix(surfaceToImageMatrix); |
|
1215 pixelPipe.prepareSpanUniforms(aa); |
|
1216 rasterizer.setup(0, 0, drawable->getWidth(), drawable->getHeight(), VG_EVEN_ODD, &pixelPipe); |
|
1217 |
|
1218 rasterizer.addEdge(Vector2(p0.x,p0.y), Vector2(p1.x,p1.y)); //throws bad_alloc |
|
1219 rasterizer.addEdge(Vector2(p1.x,p1.y), Vector2(p2.x,p2.y)); //throws bad_alloc |
|
1220 rasterizer.addEdge(Vector2(p2.x,p2.y), Vector2(p3.x,p3.y)); //throws bad_alloc |
|
1221 rasterizer.addEdge(Vector2(p3.x,p3.y), Vector2(p0.x,p0.y)); //throws bad_alloc |
|
1222 |
|
1223 rasterizer.fill(); //throws bad_alloc |
|
1224 } |
|
1225 |
|
1226 return true; |
|
1227 } |
|
1228 |
|
1229 /*-------------------------------------------------------------------*//*! |
|
1230 * \brief Converts from multisampled format to display format. |
|
1231 * \param unsafeInput Data may contain incorrect values (user data) |
|
1232 * \return |
|
1233 * \note May throw std::bad_alloc on cases where blitting within the |
|
1234 * same buffer and overlapping regions (this may change in the |
|
1235 * future). |
|
1236 *//*-------------------------------------------------------------------*/ |
|
1237 |
|
1238 void Image::blit(VGContext* context, const Image* src, |
|
1239 int sx, int sy, int dx, int dy, int w, int h, |
|
1240 Array<Rectangle>* scissors, |
|
1241 bool dither) |
|
1242 { |
|
1243 bool overlap = false; |
|
1244 (void)dither; |
|
1245 DynamicBlitter& blitter = context->getBlitter(); |
|
1246 |
|
1247 //RI_ASSERT(!src->isInUse(this)); |
|
1248 //int isx = sx, isy = sy, idx = dx, idy = dy, iw = w, ih = h; |
|
1249 |
|
1250 computeBlitRegion(sx, sy, dx, dy, w, h, src->getWidth(), src->getHeight(), m_width, m_height); |
|
1251 |
|
1252 if(w <= 0 || h <= 0) |
|
1253 return; //zero area |
|
1254 |
|
1255 if (this->m_data == src->m_data) |
|
1256 { |
|
1257 // The images may overlap. |
|
1258 int minsx = RI_INT_MIN(sx, dx); |
|
1259 int minsy = RI_INT_MIN(sy, dy); |
|
1260 int maxsx = RI_INT_MAX(sx, dx); |
|
1261 int maxsy = RI_INT_MAX(sy, dy); |
|
1262 |
|
1263 if ((maxsx < (minsx + w)) && (maxsy < (minsy + h))) |
|
1264 { |
|
1265 overlap = true; |
|
1266 } |
|
1267 } |
|
1268 |
|
1269 if (!scissors) |
|
1270 { |
|
1271 // Currently the blitter does not support scissors |
|
1272 if (!overlap) |
|
1273 { |
|
1274 blitter.prepareBlit(this, src, sx+src->m_storageOffsetX, sy+src->m_storageOffsetY, |
|
1275 dx+m_storageOffsetX, dy+m_storageOffsetY, w, h); |
|
1276 blitter.blit(); |
|
1277 } else |
|
1278 { |
|
1279 Image temp(src->getDescriptor(), w, h, VG_IMAGE_QUALITY_NONANTIALIASED); |
|
1280 blitter.prepareBlit(&temp, src, sx+src->m_storageOffsetX, sy+src->m_storageOffsetY, 0, 0, w, h); |
|
1281 blitter.blit(); |
|
1282 blitter.prepareBlit(this, &temp, 0, 0, dx+m_storageOffsetX, dy+m_storageOffsetY, w, h); |
|
1283 blitter.blit(); |
|
1284 } |
|
1285 } else |
|
1286 { |
|
1287 // For the moment, use the generic poly-rasterizer for scissored images. |
|
1288 if (!overlap) |
|
1289 { |
|
1290 // Create a child image |
|
1291 Image blitImage((Image*)src, sx, sy, w, h); |
|
1292 Matrix3x3 tx; |
|
1293 tx.set(1, 0, dx, 0, 1, dy, 0, 0, 1); |
|
1294 |
|
1295 drawImageBody(context, &blitImage, tx, |
|
1296 VG_IMAGE_QUALITY_NONANTIALIASED, |
|
1297 VG_BLEND_SRC, |
|
1298 false, |
|
1299 false, |
|
1300 VG_DRAW_IMAGE_NORMAL); |
|
1301 } else |
|
1302 { |
|
1303 // Create a copy of the source region |
|
1304 Image temp(src->getDescriptor(), w, h, VG_IMAGE_QUALITY_NONANTIALIASED); |
|
1305 blitter.prepareBlit(&temp, src, sx, sy, 0, 0, w, h); |
|
1306 blitter.blit(); |
|
1307 |
|
1308 Image blitImage((Image*)src, sx, sy, w, h); |
|
1309 Matrix3x3 tx; |
|
1310 tx.set(1, 0, dx, 0, 1, dy, 0, 0, 1); |
|
1311 |
|
1312 drawImageBody(context, &blitImage, tx, |
|
1313 VG_IMAGE_QUALITY_NONANTIALIASED, |
|
1314 VG_BLEND_SRC, |
|
1315 false, |
|
1316 false, |
|
1317 VG_DRAW_IMAGE_NORMAL); |
|
1318 } |
|
1319 } |
|
1320 |
|
1321 } |
|
1322 |
|
1323 /*-------------------------------------------------------------------*//*! |
|
1324 * \brief Returns the color at pixel (x,y). |
|
1325 * \param |
|
1326 * \return |
|
1327 * \note |
|
1328 *//*-------------------------------------------------------------------*/ |
|
1329 |
|
1330 Color Image::readPixel(int x, int y) const |
|
1331 { |
|
1332 const RIuint32 p = readPackedPixel(x, y); |
|
1333 |
|
1334 Color c; |
|
1335 c.unpack(p, m_desc); |
|
1336 return c; |
|
1337 } |
|
1338 |
|
1339 /*-------------------------------------------------------------------*//*! |
|
1340 * \brief Writes the color to pixel (x,y). Internal color formats must |
|
1341 * match. |
|
1342 * \param |
|
1343 * \return |
|
1344 * \note |
|
1345 *//*-------------------------------------------------------------------*/ |
|
1346 |
|
1347 void Image::writePixel(int x, int y, const Color& c) |
|
1348 { |
|
1349 RI_ASSERT(c.getInternalFormat() == m_desc.internalFormat); |
|
1350 |
|
1351 RIuint32 p = c.pack(m_desc); |
|
1352 writePackedPixel(x, y, p); |
|
1353 } |
|
1354 |
|
1355 /*-------------------------------------------------------------------*//*! |
|
1356 * \brief Writes a filtered color to destination surface |
|
1357 * \param |
|
1358 * \return |
|
1359 * \note |
|
1360 *//*-------------------------------------------------------------------*/ |
|
1361 |
|
1362 void Image::writeFilteredPixel(int i, int j, const Color& color, VGbitfield channelMask) |
|
1363 { |
|
1364 //section 3.4.4: before color space conversion, premultiplied colors are |
|
1365 //clamped to alpha, and the color is converted to nonpremultiplied format |
|
1366 //section 11.2: how to deal with channel mask |
|
1367 //step 1 |
|
1368 Color f = color; |
|
1369 f.clamp(); //vgColorMatrix and vgLookups can produce colors that exceed alpha or [0,1] range |
|
1370 |
|
1371 //step 2: color space conversion |
|
1372 f.convert((Color::InternalFormat)(m_desc.internalFormat & (Color::NONLINEAR | Color::LUMINANCE))); |
|
1373 |
|
1374 //step 3: read the destination color and convert it to nonpremultiplied |
|
1375 Color d = readPixel(i,j); |
|
1376 d.unpremultiply(); |
|
1377 RI_ASSERT(d.getInternalFormat() == f.getInternalFormat()); |
|
1378 |
|
1379 //step 4: replace the destination channels specified by the channelMask (channelmask is ignored for luminance formats) |
|
1380 if(!m_desc.isLuminance()) |
|
1381 { //rgba format => use channelmask |
|
1382 if(channelMask & VG_RED) |
|
1383 d.r = f.r; |
|
1384 if(channelMask & VG_GREEN) |
|
1385 d.g = f.g; |
|
1386 if(channelMask & VG_BLUE) |
|
1387 d.b = f.b; |
|
1388 if(channelMask & VG_ALPHA) |
|
1389 d.a = f.a; |
|
1390 } |
|
1391 else d = f; |
|
1392 |
|
1393 //step 5: if destination is premultiplied, convert to premultiplied format |
|
1394 if(m_desc.isPremultiplied()) |
|
1395 d.premultiply(); |
|
1396 //write the color to destination |
|
1397 writePixel(i,j,d); |
|
1398 } |
|
1399 |
|
1400 /*-------------------------------------------------------------------*//*! |
|
1401 * \brief Reads the pixel (x,y) and converts it into an alpha mask value. |
|
1402 * \param |
|
1403 * \return |
|
1404 * \note |
|
1405 *//*-------------------------------------------------------------------*/ |
|
1406 |
|
1407 RIfloat Image::readMaskPixel(int x, int y) const |
|
1408 { |
|
1409 RI_ASSERT(m_data); |
|
1410 RI_ASSERT(x >= 0 && x < m_width); |
|
1411 RI_ASSERT(y >= 0 && y < m_height); |
|
1412 RI_ASSERT(m_referenceCount > 0); |
|
1413 |
|
1414 Color c = readPixel(x,y); |
|
1415 if(m_desc.isLuminance()) |
|
1416 { |
|
1417 return c.r; |
|
1418 } |
|
1419 else |
|
1420 { //rgba |
|
1421 if(m_desc.alphaBits) |
|
1422 return c.a; |
|
1423 return c.r; |
|
1424 } |
|
1425 } |
|
1426 |
|
1427 /*-------------------------------------------------------------------*//*! |
|
1428 * \brief Writes the alpha mask to pixel (x,y). |
|
1429 * \param |
|
1430 * \return |
|
1431 * \note Overwrites color. |
|
1432 *//*-------------------------------------------------------------------*/ |
|
1433 |
|
1434 void Image::writeMaskPixel(int x, int y, RIfloat m) |
|
1435 { |
|
1436 RI_ASSERT(m_data); |
|
1437 RI_ASSERT(x >= 0 && x < m_width); |
|
1438 RI_ASSERT(y >= 0 && y < m_height); |
|
1439 RI_ASSERT(m_referenceCount > 0); |
|
1440 |
|
1441 //if luminance or no alpha, red channel will be used, otherwise alpha channel will be used |
|
1442 writePixel(x, y, Color(m,m,m,m,m_desc.internalFormat)); |
|
1443 } |
|
1444 |
|
1445 /*-------------------------------------------------------------------*//*! |
|
1446 * \brief Reads a texel (u,v) at the given mipmap level. Tiling modes and |
|
1447 * color space conversion are applied. Outputs color in premultiplied |
|
1448 * format. |
|
1449 * \param |
|
1450 * \return |
|
1451 * \note |
|
1452 *//*-------------------------------------------------------------------*/ |
|
1453 |
|
1454 Color Image::readTexel(int u, int v, int level, VGTilingMode tilingMode, const Color& tileFillColor) const |
|
1455 { |
|
1456 const Image* image = this; |
|
1457 if( level > 0 ) |
|
1458 { |
|
1459 RI_ASSERT(false); |
|
1460 } |
|
1461 RI_ASSERT(image); |
|
1462 |
|
1463 Color p; |
|
1464 if(tilingMode == VG_TILE_FILL) |
|
1465 { |
|
1466 if(u < 0 || v < 0 || u >= image->m_width || v >= image->m_height) |
|
1467 p = tileFillColor; |
|
1468 else |
|
1469 p = image->readPixel(u, v); |
|
1470 } |
|
1471 else if(tilingMode == VG_TILE_PAD) |
|
1472 { |
|
1473 u = RI_INT_MIN(RI_INT_MAX(u,0),image->m_width-1); |
|
1474 v = RI_INT_MIN(RI_INT_MAX(v,0),image->m_height-1); |
|
1475 p = image->readPixel(u, v); |
|
1476 } |
|
1477 else if(tilingMode == VG_TILE_REPEAT) |
|
1478 { |
|
1479 u = RI_INT_MOD(u, image->m_width); |
|
1480 v = RI_INT_MOD(v, image->m_height); |
|
1481 p = image->readPixel(u, v); |
|
1482 } |
|
1483 else |
|
1484 { |
|
1485 RI_ASSERT(tilingMode == VG_TILE_REFLECT); |
|
1486 |
|
1487 u = RI_INT_MOD(u, image->m_width*2); |
|
1488 v = RI_INT_MOD(v, image->m_height*2); |
|
1489 if( u >= image->m_width ) u = image->m_width*2-1 - u; |
|
1490 if( v >= image->m_height ) v = image->m_height*2-1 - v; |
|
1491 p = image->readPixel(u, v); |
|
1492 } |
|
1493 |
|
1494 p.premultiply(); //interpolate in premultiplied format |
|
1495 return p; |
|
1496 } |
|
1497 |
|
1498 /*-------------------------------------------------------------------*//*! |
|
1499 * \brief Maps point (x,y) to an image and returns a filtered, |
|
1500 * premultiplied color value. |
|
1501 * \param |
|
1502 * \return |
|
1503 * \note |
|
1504 *//*-------------------------------------------------------------------*/ |
|
1505 |
|
1506 Color Image::resample(RIfloat x, RIfloat y, const Matrix3x3& surfaceToImage, VGImageQuality quality, VGTilingMode tilingMode, const Color& tileFillColor) //throws bad_alloc |
|
1507 { |
|
1508 RI_ASSERT(m_referenceCount > 0); |
|
1509 |
|
1510 VGbitfield aq = getAllowedQuality(); |
|
1511 aq &= (VGbitfield)quality; |
|
1512 |
|
1513 Vector3 uvw(x,y,1.0f); |
|
1514 uvw = surfaceToImage * uvw; |
|
1515 RIfloat oow = 1.0f / uvw.z; |
|
1516 uvw *= oow; |
|
1517 |
|
1518 #if 0 |
|
1519 if(aq & VG_IMAGE_QUALITY_BETTER) |
|
1520 { //EWA on mipmaps |
|
1521 makeMipMaps(); //throws bad_alloc |
|
1522 |
|
1523 Color::InternalFormat procFormat = (Color::InternalFormat)(m_desc.internalFormat | Color::PREMULTIPLIED); |
|
1524 |
|
1525 RIfloat m_pixelFilterRadius = 1.25f; |
|
1526 RIfloat m_resamplingFilterRadius = 1.25f; |
|
1527 |
|
1528 RIfloat Ux = (surfaceToImage[0][0] - uvw.x * surfaceToImage[2][0]) * oow * m_pixelFilterRadius; |
|
1529 RIfloat Vx = (surfaceToImage[1][0] - uvw.y * surfaceToImage[2][0]) * oow * m_pixelFilterRadius; |
|
1530 RIfloat Uy = (surfaceToImage[0][1] - uvw.x * surfaceToImage[2][1]) * oow * m_pixelFilterRadius; |
|
1531 RIfloat Vy = (surfaceToImage[1][1] - uvw.y * surfaceToImage[2][1]) * oow * m_pixelFilterRadius; |
|
1532 RIfloat U0 = uvw.x; |
|
1533 RIfloat V0 = uvw.y; |
|
1534 |
|
1535 //calculate mip level |
|
1536 int level = 0; |
|
1537 RIfloat axis1sq = Ux*Ux + Vx*Vx; |
|
1538 RIfloat axis2sq = Uy*Uy + Vy*Vy; |
|
1539 RIfloat minorAxissq = RI_MIN(axis1sq,axis2sq); |
|
1540 while(minorAxissq > 9.0f && level < m_mipmaps.size()) //half the minor axis must be at least three texels |
|
1541 { |
|
1542 level++; |
|
1543 minorAxissq *= 0.25f; |
|
1544 } |
|
1545 |
|
1546 RIfloat sx = 1.0f; |
|
1547 RIfloat sy = 1.0f; |
|
1548 if(level > 0) |
|
1549 { |
|
1550 sx = (RIfloat)m_mipmaps[level-1]->m_width / (RIfloat)m_width; |
|
1551 sy = (RIfloat)m_mipmaps[level-1]->m_height / (RIfloat)m_height; |
|
1552 } |
|
1553 Ux *= sx; |
|
1554 Vx *= sx; |
|
1555 U0 *= sx; |
|
1556 Uy *= sy; |
|
1557 Vy *= sy; |
|
1558 V0 *= sy; |
|
1559 |
|
1560 //clamp filter size so that filtering doesn't take excessive amount of time (clamping results in aliasing) |
|
1561 RIfloat lim = 100.0f; |
|
1562 axis1sq = Ux*Ux + Vx*Vx; |
|
1563 axis2sq = Uy*Uy + Vy*Vy; |
|
1564 if( axis1sq > lim*lim ) |
|
1565 { |
|
1566 RIfloat s = lim / (RIfloat)sqrt(axis1sq); |
|
1567 Ux *= s; |
|
1568 Vx *= s; |
|
1569 } |
|
1570 if( axis2sq > lim*lim ) |
|
1571 { |
|
1572 RIfloat s = lim / (RIfloat)sqrt(axis2sq); |
|
1573 Uy *= s; |
|
1574 Vy *= s; |
|
1575 } |
|
1576 |
|
1577 |
|
1578 //form elliptic filter by combining texel and pixel filters |
|
1579 RIfloat A = Vx*Vx + Vy*Vy + 1.0f; |
|
1580 RIfloat B = -2.0f*(Ux*Vx + Uy*Vy); |
|
1581 RIfloat C = Ux*Ux + Uy*Uy + 1.0f; |
|
1582 //scale by the user-defined size of the kernel |
|
1583 A *= m_resamplingFilterRadius; |
|
1584 B *= m_resamplingFilterRadius; |
|
1585 C *= m_resamplingFilterRadius; |
|
1586 |
|
1587 //calculate bounding box in texture space |
|
1588 RIfloat usize = (RIfloat)sqrt(C); |
|
1589 RIfloat vsize = (RIfloat)sqrt(A); |
|
1590 int u1 = (int)floor(U0 - usize + 0.5f); |
|
1591 int u2 = (int)floor(U0 + usize + 0.5f); |
|
1592 int v1 = (int)floor(V0 - vsize + 0.5f); |
|
1593 int v2 = (int)floor(V0 + vsize + 0.5f); |
|
1594 if( u1 == u2 || v1 == v2 ) |
|
1595 return Color(0,0,0,0,procFormat); |
|
1596 |
|
1597 //scale the filter so that Q = 1 at the cutoff radius |
|
1598 RIfloat F = A*C - 0.25f * B*B; |
|
1599 if( F <= 0.0f ) |
|
1600 return Color(0,0,0,0,procFormat); //invalid filter shape due to numerical inaccuracies => return black |
|
1601 RIfloat ooF = 1.0f / F; |
|
1602 A *= ooF; |
|
1603 B *= ooF; |
|
1604 C *= ooF; |
|
1605 |
|
1606 //evaluate filter by using forward differences to calculate Q = A*U^2 + B*U*V + C*V^2 |
|
1607 Color color(0,0,0,0,procFormat); |
|
1608 RIfloat sumweight = 0.0f; |
|
1609 RIfloat DDQ = 2.0f * A; |
|
1610 RIfloat U = (RIfloat)u1 - U0 + 0.5f; |
|
1611 for(int v=v1;v<v2;v++) |
|
1612 { |
|
1613 RIfloat V = (RIfloat)v - V0 + 0.5f; |
|
1614 RIfloat DQ = A*(2.0f*U+1.0f) + B*V; |
|
1615 RIfloat Q = (C*V+B*U)*V + A*U*U; |
|
1616 for(int u=u1;u<u2;u++) |
|
1617 { |
|
1618 if( Q >= 0.0f && Q < 1.0f ) |
|
1619 { //Q = r^2, fit gaussian to the range [0,1] |
|
1620 RIfloat weight = (RIfloat)exp(-0.5f * 10.0f * Q); //gaussian at radius 10 equals 0.0067 |
|
1621 color += weight * readTexel(u, v, level, tilingMode, tileFillColor); |
|
1622 sumweight += weight; |
|
1623 } |
|
1624 Q += DQ; |
|
1625 DQ += DDQ; |
|
1626 } |
|
1627 } |
|
1628 if( sumweight == 0.0f ) |
|
1629 return Color(0,0,0,0,procFormat); |
|
1630 RI_ASSERT(sumweight > 0.0f); |
|
1631 sumweight = 1.0f / sumweight; |
|
1632 return color * sumweight; |
|
1633 } |
|
1634 else |
|
1635 #endif |
|
1636 //if(aq & VG_IMAGE_QUALITY_FASTER) |
|
1637 if(aq & VG_IMAGE_QUALITY_BETTER) |
|
1638 { //bilinear |
|
1639 uvw.x -= 0.5f; |
|
1640 uvw.y -= 0.5f; |
|
1641 int u = (int)floor(uvw.x); |
|
1642 int v = (int)floor(uvw.y); |
|
1643 Color c00 = readTexel(u,v, 0, tilingMode, tileFillColor); |
|
1644 Color c10 = readTexel(u+1,v, 0, tilingMode, tileFillColor); |
|
1645 Color c01 = readTexel(u,v+1, 0, tilingMode, tileFillColor); |
|
1646 Color c11 = readTexel(u+1,v+1, 0, tilingMode, tileFillColor); |
|
1647 RIfloat fu = uvw.x - (RIfloat)u; |
|
1648 RIfloat fv = uvw.y - (RIfloat)v; |
|
1649 Color c0 = c00 * (1.0f - fu) + c10 * fu; |
|
1650 Color c1 = c01 * (1.0f - fu) + c11 * fu; |
|
1651 return c0 * (1.0f - fv) + c1 * fv; |
|
1652 } |
|
1653 else //VG_IMAGE_QUALITY_FASTER and VG_IMAGE_QUALITY_NONANTIALIASED |
|
1654 { //point sampling |
|
1655 return readTexel((int)floor(uvw.x), (int)floor(uvw.y), 0, tilingMode, tileFillColor); |
|
1656 } |
|
1657 } |
|
1658 |
|
1659 /*-------------------------------------------------------------------*//*! |
|
1660 * \brief Applies color matrix filter. |
|
1661 * \param |
|
1662 * \return |
|
1663 * \note |
|
1664 *//*-------------------------------------------------------------------*/ |
|
1665 |
|
1666 void Image::colorMatrix(const Image& src, const RIfloat* matrix, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask) |
|
1667 { |
|
1668 RI_ASSERT(src.m_data); //source exists |
|
1669 RI_ASSERT(m_data); //destination exists |
|
1670 RI_ASSERT(matrix); |
|
1671 RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); |
|
1672 |
|
1673 int w = RI_INT_MIN(m_width, src.m_width); |
|
1674 int h = RI_INT_MIN(m_height, src.m_height); |
|
1675 RI_ASSERT(w > 0 && h > 0); |
|
1676 |
|
1677 Color::InternalFormat srcFormat = src.m_desc.internalFormat; |
|
1678 Color::InternalFormat procFormat = (Color::InternalFormat)(srcFormat & ~Color::LUMINANCE); //process in RGB, not luminance |
|
1679 if(filterFormatLinear) |
|
1680 procFormat = (Color::InternalFormat)(procFormat & ~Color::NONLINEAR); |
|
1681 else |
|
1682 procFormat = (Color::InternalFormat)(procFormat | Color::NONLINEAR); |
|
1683 |
|
1684 if(filterFormatPremultiplied) |
|
1685 procFormat = (Color::InternalFormat)(procFormat | Color::PREMULTIPLIED); |
|
1686 else |
|
1687 procFormat = (Color::InternalFormat)(procFormat & ~Color::PREMULTIPLIED); |
|
1688 |
|
1689 for(int j=0;j<h;j++) |
|
1690 { |
|
1691 for(int i=0;i<w;i++) |
|
1692 { |
|
1693 Color s = src.readPixel(i,j); //convert to RGBA [0,1] |
|
1694 s.convert(procFormat); |
|
1695 |
|
1696 Color d(0,0,0,0,procFormat); |
|
1697 d.r = matrix[0+4*0] * s.r + matrix[0+4*1] * s.g + matrix[0+4*2] * s.b + matrix[0+4*3] * s.a + matrix[0+4*4]; |
|
1698 d.g = matrix[1+4*0] * s.r + matrix[1+4*1] * s.g + matrix[1+4*2] * s.b + matrix[1+4*3] * s.a + matrix[1+4*4]; |
|
1699 d.b = matrix[2+4*0] * s.r + matrix[2+4*1] * s.g + matrix[2+4*2] * s.b + matrix[2+4*3] * s.a + matrix[2+4*4]; |
|
1700 d.a = matrix[3+4*0] * s.r + matrix[3+4*1] * s.g + matrix[3+4*2] * s.b + matrix[3+4*3] * s.a + matrix[3+4*4]; |
|
1701 |
|
1702 writeFilteredPixel(i, j, d, channelMask); |
|
1703 } |
|
1704 } |
|
1705 } |
|
1706 |
|
1707 /*-------------------------------------------------------------------*//*! |
|
1708 * \brief Reads a pixel from image with tiling mode applied. |
|
1709 * \param |
|
1710 * \return |
|
1711 * \note |
|
1712 *//*-------------------------------------------------------------------*/ |
|
1713 |
|
1714 static Color readTiledPixel(int x, int y, int w, int h, VGTilingMode tilingMode, const Array<Color>& image, const Color& edge) |
|
1715 { |
|
1716 Color s; |
|
1717 if(x < 0 || x >= w || y < 0 || y >= h) |
|
1718 { //apply tiling mode |
|
1719 switch(tilingMode) |
|
1720 { |
|
1721 case VG_TILE_FILL: |
|
1722 s = edge; |
|
1723 break; |
|
1724 case VG_TILE_PAD: |
|
1725 x = RI_INT_MIN(RI_INT_MAX(x, 0), w-1); |
|
1726 y = RI_INT_MIN(RI_INT_MAX(y, 0), h-1); |
|
1727 RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h); |
|
1728 s = image[y*w+x]; |
|
1729 break; |
|
1730 case VG_TILE_REPEAT: |
|
1731 x = RI_INT_MOD(x, w); |
|
1732 y = RI_INT_MOD(y, h); |
|
1733 RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h); |
|
1734 s = image[y*w+x]; |
|
1735 break; |
|
1736 default: |
|
1737 RI_ASSERT(tilingMode == VG_TILE_REFLECT); |
|
1738 x = RI_INT_MOD(x, w*2); |
|
1739 y = RI_INT_MOD(y, h*2); |
|
1740 if(x >= w) x = w*2-1-x; |
|
1741 if(y >= h) y = h*2-1-y; |
|
1742 RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h); |
|
1743 s = image[y*w+x]; |
|
1744 break; |
|
1745 } |
|
1746 } |
|
1747 else |
|
1748 { |
|
1749 RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h); |
|
1750 s = image[y*w+x]; |
|
1751 } |
|
1752 return s; |
|
1753 } |
|
1754 |
|
1755 /*-------------------------------------------------------------------*//*! |
|
1756 * \brief Returns processing format for filtering. |
|
1757 * \param |
|
1758 * \return |
|
1759 * \note |
|
1760 *//*-------------------------------------------------------------------*/ |
|
1761 |
|
1762 static Color::InternalFormat getProcessingFormat(Color::InternalFormat srcFormat, bool filterFormatLinear, bool filterFormatPremultiplied) |
|
1763 { |
|
1764 Color::InternalFormat procFormat = (Color::InternalFormat)(srcFormat & ~Color::LUMINANCE); //process in RGB, not luminance |
|
1765 if(filterFormatLinear) |
|
1766 procFormat = (Color::InternalFormat)(procFormat & ~Color::NONLINEAR); |
|
1767 else |
|
1768 procFormat = (Color::InternalFormat)(procFormat | Color::NONLINEAR); |
|
1769 |
|
1770 if(filterFormatPremultiplied) |
|
1771 procFormat = (Color::InternalFormat)(procFormat | Color::PREMULTIPLIED); |
|
1772 else |
|
1773 procFormat = (Color::InternalFormat)(procFormat & ~Color::PREMULTIPLIED); |
|
1774 return procFormat; |
|
1775 } |
|
1776 |
|
1777 /*-------------------------------------------------------------------*//*! |
|
1778 * \brief Applies convolution filter. |
|
1779 * \param |
|
1780 * \return |
|
1781 * \note |
|
1782 *//*-------------------------------------------------------------------*/ |
|
1783 |
|
1784 void Image::convolve(const Image& src, int kernelWidth, int kernelHeight, int shiftX, int shiftY, const RIint16* kernel, RIfloat scale, RIfloat bias, VGTilingMode tilingMode, const Color& edgeFillColor, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask) |
|
1785 { |
|
1786 RI_ASSERT(src.m_data); //source exists |
|
1787 RI_ASSERT(m_data); //destination exists |
|
1788 RI_ASSERT(kernel && kernelWidth > 0 && kernelHeight > 0); |
|
1789 RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); |
|
1790 |
|
1791 //the area to be written is an intersection of source and destination image areas. |
|
1792 //lower-left corners of the images are aligned. |
|
1793 int w = RI_INT_MIN(m_width, src.m_width); |
|
1794 int h = RI_INT_MIN(m_height, src.m_height); |
|
1795 RI_ASSERT(w > 0 && h > 0); |
|
1796 |
|
1797 Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); |
|
1798 |
|
1799 Color edge = edgeFillColor; |
|
1800 edge.clamp(); |
|
1801 edge.convert(procFormat); |
|
1802 |
|
1803 Array<Color> tmp; |
|
1804 tmp.resize(src.m_width*src.m_height); //throws bad_alloc |
|
1805 |
|
1806 //copy source region to tmp and do conversion |
|
1807 for(int j=0;j<src.m_height;j++) |
|
1808 { |
|
1809 for(int i=0;i<src.m_width;i++) |
|
1810 { |
|
1811 Color s = src.readPixel(i, j); |
|
1812 s.convert(procFormat); |
|
1813 tmp[j*src.m_width+i] = s; |
|
1814 } |
|
1815 } |
|
1816 |
|
1817 for(int j=0;j<h;j++) |
|
1818 { |
|
1819 for(int i=0;i<w;i++) |
|
1820 { |
|
1821 Color sum(0,0,0,0,procFormat); |
|
1822 |
|
1823 for(int kj=0;kj<kernelHeight;kj++) |
|
1824 { |
|
1825 for(int ki=0;ki<kernelWidth;ki++) |
|
1826 { |
|
1827 int x = i+ki-shiftX; |
|
1828 int y = j+kj-shiftY; |
|
1829 Color s = readTiledPixel(x, y, src.m_width, src.m_height, tilingMode, tmp, edge); |
|
1830 |
|
1831 int kx = kernelWidth-ki-1; |
|
1832 int ky = kernelHeight-kj-1; |
|
1833 RI_ASSERT(kx >= 0 && kx < kernelWidth && ky >= 0 && ky < kernelHeight); |
|
1834 |
|
1835 sum += (RIfloat)kernel[kx*kernelHeight+ky] * s; |
|
1836 } |
|
1837 } |
|
1838 |
|
1839 sum *= scale; |
|
1840 sum.r += bias; |
|
1841 sum.g += bias; |
|
1842 sum.b += bias; |
|
1843 sum.a += bias; |
|
1844 |
|
1845 writeFilteredPixel(i, j, sum, channelMask); |
|
1846 } |
|
1847 } |
|
1848 } |
|
1849 |
|
1850 /*-------------------------------------------------------------------*//*! |
|
1851 * \brief Applies separable convolution filter. |
|
1852 * \param |
|
1853 * \return |
|
1854 * \note |
|
1855 *//*-------------------------------------------------------------------*/ |
|
1856 |
|
1857 void Image::separableConvolve(const Image& src, int kernelWidth, int kernelHeight, int shiftX, int shiftY, const RIint16* kernelX, const RIint16* kernelY, RIfloat scale, RIfloat bias, VGTilingMode tilingMode, const Color& edgeFillColor, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask) |
|
1858 { |
|
1859 RI_ASSERT(src.m_data); //source exists |
|
1860 RI_ASSERT(m_data); //destination exists |
|
1861 RI_ASSERT(kernelX && kernelY && kernelWidth > 0 && kernelHeight > 0); |
|
1862 RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); |
|
1863 |
|
1864 //the area to be written is an intersection of source and destination image areas. |
|
1865 //lower-left corners of the images are aligned. |
|
1866 int w = RI_INT_MIN(m_width, src.m_width); |
|
1867 int h = RI_INT_MIN(m_height, src.m_height); |
|
1868 RI_ASSERT(w > 0 && h > 0); |
|
1869 |
|
1870 Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); |
|
1871 |
|
1872 Color edge = edgeFillColor; |
|
1873 edge.clamp(); |
|
1874 edge.convert(procFormat); |
|
1875 |
|
1876 Array<Color> tmp; |
|
1877 tmp.resize(src.m_width*src.m_height); //throws bad_alloc |
|
1878 |
|
1879 //copy source region to tmp and do conversion |
|
1880 for(int j=0;j<src.m_height;j++) |
|
1881 { |
|
1882 for(int i=0;i<src.m_width;i++) |
|
1883 { |
|
1884 Color s = src.readPixel(i, j); |
|
1885 s.convert(procFormat); |
|
1886 tmp[j*src.m_width+i] = s; |
|
1887 } |
|
1888 } |
|
1889 |
|
1890 Array<Color> tmp2; |
|
1891 tmp2.resize(w*src.m_height); //throws bad_alloc |
|
1892 for(int j=0;j<src.m_height;j++) |
|
1893 { |
|
1894 for(int i=0;i<w;i++) |
|
1895 { |
|
1896 Color sum(0,0,0,0,procFormat); |
|
1897 for(int ki=0;ki<kernelWidth;ki++) |
|
1898 { |
|
1899 int x = i+ki-shiftX; |
|
1900 Color s = readTiledPixel(x, j, src.m_width, src.m_height, tilingMode, tmp, edge); |
|
1901 |
|
1902 int kx = kernelWidth-ki-1; |
|
1903 RI_ASSERT(kx >= 0 && kx < kernelWidth); |
|
1904 |
|
1905 sum += (RIfloat)kernelX[kx] * s; |
|
1906 } |
|
1907 tmp2[j*w+i] = sum; |
|
1908 } |
|
1909 } |
|
1910 |
|
1911 if(tilingMode == VG_TILE_FILL) |
|
1912 { //convolve the edge color |
|
1913 Color sum(0,0,0,0,procFormat); |
|
1914 for(int ki=0;ki<kernelWidth;ki++) |
|
1915 { |
|
1916 sum += (RIfloat)kernelX[ki] * edge; |
|
1917 } |
|
1918 edge = sum; |
|
1919 } |
|
1920 |
|
1921 for(int j=0;j<h;j++) |
|
1922 { |
|
1923 for(int i=0;i<w;i++) |
|
1924 { |
|
1925 Color sum(0,0,0,0,procFormat); |
|
1926 for(int kj=0;kj<kernelHeight;kj++) |
|
1927 { |
|
1928 int y = j+kj-shiftY; |
|
1929 Color s = readTiledPixel(i, y, w, src.m_height, tilingMode, tmp2, edge); |
|
1930 |
|
1931 int ky = kernelHeight-kj-1; |
|
1932 RI_ASSERT(ky >= 0 && ky < kernelHeight); |
|
1933 |
|
1934 sum += (RIfloat)kernelY[ky] * s; |
|
1935 } |
|
1936 |
|
1937 sum *= scale; |
|
1938 sum.r += bias; |
|
1939 sum.g += bias; |
|
1940 sum.b += bias; |
|
1941 sum.a += bias; |
|
1942 |
|
1943 writeFilteredPixel(i, j, sum, channelMask); |
|
1944 } |
|
1945 } |
|
1946 } |
|
1947 |
|
1948 /*-------------------------------------------------------------------*//*! |
|
1949 * \brief Applies Gaussian blur filter. |
|
1950 * \param |
|
1951 * \return |
|
1952 * \note |
|
1953 *//*-------------------------------------------------------------------*/ |
|
1954 |
|
1955 void Image::gaussianBlur(const Image& src, RIfloat stdDeviationX, RIfloat stdDeviationY, VGTilingMode tilingMode, const Color& edgeFillColor, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask) |
|
1956 { |
|
1957 RI_ASSERT(src.m_data); //source exists |
|
1958 RI_ASSERT(m_data); //destination exists |
|
1959 RI_ASSERT(stdDeviationX > 0.0f && stdDeviationY > 0.0f); |
|
1960 RI_ASSERT(stdDeviationX <= RI_MAX_GAUSSIAN_STD_DEVIATION && stdDeviationY <= RI_MAX_GAUSSIAN_STD_DEVIATION); |
|
1961 RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); |
|
1962 |
|
1963 //the area to be written is an intersection of source and destination image areas. |
|
1964 //lower-left corners of the images are aligned. |
|
1965 int w = RI_INT_MIN(m_width, src.m_width); |
|
1966 int h = RI_INT_MIN(m_height, src.m_height); |
|
1967 RI_ASSERT(w > 0 && h > 0); |
|
1968 |
|
1969 Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); |
|
1970 |
|
1971 Color edge = edgeFillColor; |
|
1972 edge.clamp(); |
|
1973 edge.convert(procFormat); |
|
1974 |
|
1975 Array<Color> tmp; |
|
1976 tmp.resize(src.m_width*src.m_height); //throws bad_alloc |
|
1977 |
|
1978 //copy source region to tmp and do conversion |
|
1979 for(int j=0;j<src.m_height;j++) |
|
1980 { |
|
1981 for(int i=0;i<src.m_width;i++) |
|
1982 { |
|
1983 Color s = src.readPixel(i, j); |
|
1984 s.convert(procFormat); |
|
1985 tmp[j*src.m_width+i] = s; |
|
1986 } |
|
1987 } |
|
1988 |
|
1989 RIfloat expScaleX = -1.0f / (2.0f*stdDeviationX*stdDeviationX); |
|
1990 RIfloat expScaleY = -1.0f / (2.0f*stdDeviationY*stdDeviationY); |
|
1991 |
|
1992 int kernelWidth = (int)(stdDeviationX * 4.0f + 1.0f); |
|
1993 int kernelHeight = (int)(stdDeviationY * 4.0f + 1.0f); |
|
1994 |
|
1995 //make a separable kernel |
|
1996 Array<RIfloat> kernelX; |
|
1997 kernelX.resize(kernelWidth*2+1); |
|
1998 int shiftX = kernelWidth; |
|
1999 RIfloat scaleX = 0.0f; |
|
2000 for(int i=0;i<kernelX.size();i++) |
|
2001 { |
|
2002 int x = i-shiftX; |
|
2003 kernelX[i] = (RIfloat)exp((RIfloat)x*(RIfloat)x * expScaleX); |
|
2004 scaleX += kernelX[i]; |
|
2005 } |
|
2006 scaleX = 1.0f / scaleX; //NOTE: using the mathematical definition of the scaling term doesn't work since we cut the filter support early for performance |
|
2007 |
|
2008 Array<RIfloat> kernelY; |
|
2009 kernelY.resize(kernelHeight*2+1); |
|
2010 int shiftY = kernelHeight; |
|
2011 RIfloat scaleY = 0.0f; |
|
2012 for(int i=0;i<kernelY.size();i++) |
|
2013 { |
|
2014 int y = i-shiftY; |
|
2015 kernelY[i] = (RIfloat)exp((RIfloat)y*(RIfloat)y * expScaleY); |
|
2016 scaleY += kernelY[i]; |
|
2017 } |
|
2018 scaleY = 1.0f / scaleY; //NOTE: using the mathematical definition of the scaling term doesn't work since we cut the filter support early for performance |
|
2019 |
|
2020 Array<Color> tmp2; |
|
2021 tmp2.resize(w*src.m_height); //throws bad_alloc |
|
2022 //horizontal pass |
|
2023 for(int j=0;j<src.m_height;j++) |
|
2024 { |
|
2025 for(int i=0;i<w;i++) |
|
2026 { |
|
2027 Color sum(0,0,0,0,procFormat); |
|
2028 for(int ki=0;ki<kernelX.size();ki++) |
|
2029 { |
|
2030 int x = i+ki-shiftX; |
|
2031 sum += kernelX[ki] * readTiledPixel(x, j, src.m_width, src.m_height, tilingMode, tmp, edge); |
|
2032 } |
|
2033 tmp2[j*w+i] = sum * scaleX; |
|
2034 } |
|
2035 } |
|
2036 //vertical pass |
|
2037 for(int j=0;j<h;j++) |
|
2038 { |
|
2039 for(int i=0;i<w;i++) |
|
2040 { |
|
2041 Color sum(0,0,0,0,procFormat); |
|
2042 for(int kj=0;kj<kernelY.size();kj++) |
|
2043 { |
|
2044 int y = j+kj-shiftY; |
|
2045 sum += kernelY[kj] * readTiledPixel(i, y, w, src.m_height, tilingMode, tmp2, edge); |
|
2046 } |
|
2047 writeFilteredPixel(i, j, sum * scaleY, channelMask); |
|
2048 } |
|
2049 } |
|
2050 } |
|
2051 |
|
2052 /*-------------------------------------------------------------------*//*! |
|
2053 * \brief Returns lookup table format for lookup filters. |
|
2054 * \param |
|
2055 * \return |
|
2056 * \note |
|
2057 *//*-------------------------------------------------------------------*/ |
|
2058 |
|
2059 static Color::InternalFormat getLUTFormat(bool outputLinear, bool outputPremultiplied) |
|
2060 { |
|
2061 Color::InternalFormat lutFormat = Color::lRGBA; |
|
2062 if(outputLinear && outputPremultiplied) |
|
2063 lutFormat = Color::lRGBA_PRE; |
|
2064 else if(!outputLinear && !outputPremultiplied) |
|
2065 lutFormat = Color::sRGBA; |
|
2066 else if(!outputLinear && outputPremultiplied) |
|
2067 lutFormat = Color::sRGBA_PRE; |
|
2068 return lutFormat; |
|
2069 } |
|
2070 |
|
2071 /*-------------------------------------------------------------------*//*! |
|
2072 * \brief Applies multi-channel lookup table filter. |
|
2073 * \param |
|
2074 * \return |
|
2075 * \note |
|
2076 *//*-------------------------------------------------------------------*/ |
|
2077 |
|
2078 void Image::lookup(const Image& src, const RIuint8 * redLUT, const RIuint8 * greenLUT, const RIuint8 * blueLUT, const RIuint8 * alphaLUT, bool outputLinear, bool outputPremultiplied, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask) |
|
2079 { |
|
2080 RI_ASSERT(src.m_data); //source exists |
|
2081 RI_ASSERT(m_data); //destination exists |
|
2082 RI_ASSERT(redLUT && greenLUT && blueLUT && alphaLUT); |
|
2083 RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); |
|
2084 |
|
2085 //the area to be written is an intersection of source and destination image areas. |
|
2086 //lower-left corners of the images are aligned. |
|
2087 int w = RI_INT_MIN(m_width, src.m_width); |
|
2088 int h = RI_INT_MIN(m_height, src.m_height); |
|
2089 RI_ASSERT(w > 0 && h > 0); |
|
2090 |
|
2091 Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); |
|
2092 Color::InternalFormat lutFormat = getLUTFormat(outputLinear, outputPremultiplied); |
|
2093 |
|
2094 for(int j=0;j<h;j++) |
|
2095 { |
|
2096 for(int i=0;i<w;i++) |
|
2097 { |
|
2098 Color s = src.readPixel(i,j); //convert to RGBA [0,1] |
|
2099 s.convert(procFormat); |
|
2100 |
|
2101 Color d(0,0,0,0,lutFormat); |
|
2102 d.r = intToColor( redLUT[colorToInt(s.r, 255)], 255); |
|
2103 d.g = intToColor(greenLUT[colorToInt(s.g, 255)], 255); |
|
2104 d.b = intToColor( blueLUT[colorToInt(s.b, 255)], 255); |
|
2105 d.a = intToColor(alphaLUT[colorToInt(s.a, 255)], 255); |
|
2106 |
|
2107 writeFilteredPixel(i, j, d, channelMask); |
|
2108 } |
|
2109 } |
|
2110 } |
|
2111 |
|
2112 /*-------------------------------------------------------------------*//*! |
|
2113 * \brief Applies single channel lookup table filter. |
|
2114 * \param |
|
2115 * \return |
|
2116 * \note |
|
2117 *//*-------------------------------------------------------------------*/ |
|
2118 |
|
2119 void Image::lookupSingle(const Image& src, const RIuint32 * lookupTable, VGImageChannel sourceChannel, bool outputLinear, bool outputPremultiplied, bool filterFormatLinear, bool filterFormatPremultiplied, VGbitfield channelMask) |
|
2120 { |
|
2121 RI_ASSERT(src.m_data); //source exists |
|
2122 RI_ASSERT(m_data); //destination exists |
|
2123 RI_ASSERT(lookupTable); |
|
2124 RI_ASSERT(m_referenceCount > 0 && src.m_referenceCount > 0); |
|
2125 |
|
2126 //the area to be written is an intersection of source and destination image areas. |
|
2127 //lower-left corners of the images are aligned. |
|
2128 int w = RI_INT_MIN(m_width, src.m_width); |
|
2129 int h = RI_INT_MIN(m_height, src.m_height); |
|
2130 RI_ASSERT(w > 0 && h > 0); |
|
2131 |
|
2132 if(src.m_desc.isLuminance()) |
|
2133 sourceChannel = VG_RED; |
|
2134 else if(src.m_desc.redBits + src.m_desc.greenBits + src.m_desc.blueBits == 0) |
|
2135 { |
|
2136 RI_ASSERT(src.m_desc.alphaBits); |
|
2137 sourceChannel = VG_ALPHA; |
|
2138 } |
|
2139 |
|
2140 Color::InternalFormat procFormat = getProcessingFormat(src.m_desc.internalFormat, filterFormatLinear, filterFormatPremultiplied); |
|
2141 Color::InternalFormat lutFormat = getLUTFormat(outputLinear, outputPremultiplied); |
|
2142 |
|
2143 for(int j=0;j<h;j++) |
|
2144 { |
|
2145 for(int i=0;i<w;i++) |
|
2146 { |
|
2147 Color s = src.readPixel(i,j); //convert to RGBA [0,1] |
|
2148 s.convert(procFormat); |
|
2149 int e; |
|
2150 switch(sourceChannel) |
|
2151 { |
|
2152 case VG_RED: |
|
2153 e = colorToInt(s.r, 255); |
|
2154 break; |
|
2155 case VG_GREEN: |
|
2156 e = colorToInt(s.g, 255); |
|
2157 break; |
|
2158 case VG_BLUE: |
|
2159 e = colorToInt(s.b, 255); |
|
2160 break; |
|
2161 default: |
|
2162 RI_ASSERT(sourceChannel == VG_ALPHA); |
|
2163 e = colorToInt(s.a, 255); |
|
2164 break; |
|
2165 } |
|
2166 |
|
2167 RIuint32 l = ((const RIuint32*)lookupTable)[e]; |
|
2168 Color d(0,0,0,0,lutFormat); |
|
2169 d.r = intToColor((l>>24), 255); |
|
2170 d.g = intToColor((l>>16), 255); |
|
2171 d.b = intToColor((l>> 8), 255); |
|
2172 d.a = intToColor((l ), 255); |
|
2173 |
|
2174 writeFilteredPixel(i, j, d, channelMask); |
|
2175 } |
|
2176 } |
|
2177 } |
|
2178 |
|
2179 |
|
2180 /*-------------------------------------------------------------------*//*! |
|
2181 * \brief |
|
2182 * \param |
|
2183 * \return |
|
2184 * \note |
|
2185 *//*-------------------------------------------------------------------*/ |
|
2186 |
|
2187 Surface::Surface(const Color::Descriptor& desc, int width, int height, int numSamples) : |
|
2188 m_width(width), |
|
2189 m_height(height), |
|
2190 m_numSamples(numSamples), |
|
2191 m_referenceCount(0), |
|
2192 m_image(NULL) |
|
2193 { |
|
2194 RI_ASSERT(width > 0 && height > 0 && numSamples > 0 && numSamples <= 32); |
|
2195 m_image = RI_NEW(Image, (desc, width*numSamples, height, 0)); //throws bad_alloc |
|
2196 m_image->addReference(); |
|
2197 } |
|
2198 |
|
2199 /*-------------------------------------------------------------------*//*! |
|
2200 * \brief |
|
2201 * \param |
|
2202 * \return |
|
2203 * \note |
|
2204 *//*-------------------------------------------------------------------*/ |
|
2205 |
|
2206 Surface::Surface(Image* image) : |
|
2207 m_width(0), |
|
2208 m_height(0), |
|
2209 m_numSamples(1), |
|
2210 m_referenceCount(0), |
|
2211 m_image(image) |
|
2212 { |
|
2213 RI_ASSERT(image); |
|
2214 m_width = image->getWidth(); |
|
2215 m_height = image->getHeight(); |
|
2216 m_image->addReference(); |
|
2217 } |
|
2218 |
|
2219 /*-------------------------------------------------------------------*//*! |
|
2220 * \brief |
|
2221 * \param |
|
2222 * \return |
|
2223 * \note |
|
2224 *//*-------------------------------------------------------------------*/ |
|
2225 |
|
2226 Surface::Surface(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data) : |
|
2227 m_width(width), |
|
2228 m_height(height), |
|
2229 m_numSamples(1), |
|
2230 m_referenceCount(0), |
|
2231 m_image(NULL) |
|
2232 { |
|
2233 RI_ASSERT(width > 0 && height > 0); |
|
2234 m_image = RI_NEW(Image, (desc, width, height, stride, data)); //throws bad_alloc |
|
2235 m_image->addReference(); |
|
2236 } |
|
2237 |
|
2238 /*-------------------------------------------------------------------*//*! |
|
2239 * \brief |
|
2240 * \param |
|
2241 * \return |
|
2242 * \note |
|
2243 *//*-------------------------------------------------------------------*/ |
|
2244 |
|
2245 Surface::~Surface() |
|
2246 { |
|
2247 RI_ASSERT(m_referenceCount == 0); |
|
2248 if(!m_image->removeReference()) |
|
2249 RI_DELETE(m_image); |
|
2250 } |
|
2251 |
|
2252 /*-------------------------------------------------------------------*//*! |
|
2253 * \brief |
|
2254 * \param |
|
2255 * \return |
|
2256 * \note |
|
2257 *//*-------------------------------------------------------------------*/ |
|
2258 |
|
2259 void Surface::clear(const Color& clearColor, int x, int y, int w, int h, const Array<Rectangle>* scissors) |
|
2260 { |
|
2261 RI_ASSERT(m_numSamples == 1); |
|
2262 |
|
2263 Image* image = m_image; |
|
2264 |
|
2265 Color col = clearColor; |
|
2266 col.clamp(); |
|
2267 col.convert(m_image->getDescriptor().internalFormat); |
|
2268 |
|
2269 IntegerColor ic = IntegerColor(col); |
|
2270 ic.truncateColor(m_image->getDescriptor()); |
|
2271 const RIuint32 c = ic.getPackedColor(m_image->getDescriptor()); |
|
2272 |
|
2273 if (x != 0 || y != 0 || w != image->getWidth() || h != image->getHeight() || scissors) |
|
2274 { |
|
2275 // Simple implementation: intersect with surface and clip rects -> may clear the |
|
2276 // same area several times. Best if scissors are non-overlapping |
|
2277 Rectangle r(0,0,getWidth(),getHeight()); |
|
2278 r.intersect(Rectangle(x,y,w,h)); |
|
2279 |
|
2280 if (r.isEmpty() || (scissors && scissors->size() == 0)) |
|
2281 return; |
|
2282 |
|
2283 if (scissors && scissors->size()) |
|
2284 { |
|
2285 for (int i = 0; i < scissors->size(); i++) |
|
2286 { |
|
2287 Rectangle s = (*scissors)[i]; |
|
2288 s.intersect(r); |
|
2289 |
|
2290 if (s.isEmpty()) |
|
2291 continue; |
|
2292 |
|
2293 image->fillPackedRectangle(s.x, s.y, s.width, s.height, c); |
|
2294 } |
|
2295 } |
|
2296 else |
|
2297 { |
|
2298 image->fillPackedRectangle(r.x, r.y, r.width, r.height, c); |
|
2299 } |
|
2300 } |
|
2301 else |
|
2302 { |
|
2303 // Clear the whole buffer |
|
2304 |
|
2305 m_image->fillPacked(c); |
|
2306 } |
|
2307 } |
|
2308 |
|
2309 /*-------------------------------------------------------------------*//*! |
|
2310 * \brief |
|
2311 * \param |
|
2312 * \return |
|
2313 * \note |
|
2314 *//*-------------------------------------------------------------------*/ |
|
2315 |
|
2316 #if 0 |
|
2317 void Surface::blit(const Image& src, int sx, int sy, int dx, int dy, int w, int h) |
|
2318 { |
|
2319 Rectangle rect; |
|
2320 rect.x = 0; |
|
2321 rect.y = 0; |
|
2322 rect.width = getWidth(); |
|
2323 rect.height = getHeight(); |
|
2324 Array<Rectangle> scissors; |
|
2325 scissors.push_back(rect); |
|
2326 blit(src, sx, sy, dx, dy, w, h, scissors); |
|
2327 } |
|
2328 #endif |
|
2329 |
|
2330 /*-------------------------------------------------------------------*//*! |
|
2331 * \brief |
|
2332 * \param |
|
2333 * \return |
|
2334 * \note no overlap is possible. Single sample to single or multisample (replicate) |
|
2335 *//*-------------------------------------------------------------------*/ |
|
2336 |
|
2337 #if 0 |
|
2338 void Surface::blit(const Image& src, int sx, int sy, int dx, int dy, int w, int h, const Array<Rectangle>& scissors) |
|
2339 { |
|
2340 //img=>fb: vgSetPixels |
|
2341 //user=>fb: vgWritePixels |
|
2342 computeBlitRegion(sx, sy, dx, dy, w, h, src.getWidth(), src.getHeight(), getWidth(), getHeight()); |
|
2343 if(w <= 0 || h <= 0) |
|
2344 return; //zero area |
|
2345 |
|
2346 Array<ScissorEdge> scissorEdges; |
|
2347 for(int i=0;i<scissors.size();i++) |
|
2348 { |
|
2349 if(scissors[i].width > 0 && scissors[i].height > 0) |
|
2350 { |
|
2351 ScissorEdge e; |
|
2352 e.miny = scissors[i].y; |
|
2353 e.maxy = RI_INT_ADDSATURATE(scissors[i].y, scissors[i].height); |
|
2354 |
|
2355 e.x = scissors[i].x; |
|
2356 e.direction = 1; |
|
2357 scissorEdges.push_back(e); //throws bad_alloc |
|
2358 e.x = RI_INT_ADDSATURATE(scissors[i].x, scissors[i].width); |
|
2359 e.direction = -1; |
|
2360 scissorEdges.push_back(e); //throws bad_alloc |
|
2361 } |
|
2362 } |
|
2363 if(!scissorEdges.size()) |
|
2364 return; //there are no scissor rectangles => nothing is visible |
|
2365 |
|
2366 //sort scissor edges by edge x |
|
2367 scissorEdges.sort(); |
|
2368 |
|
2369 Array<ScissorEdge> scissorAet; |
|
2370 for(int j=0;j<h;j++) |
|
2371 { |
|
2372 //gather scissor edges intersecting this scanline |
|
2373 scissorAet.clear(); |
|
2374 for(int e=0;e<scissorEdges.size();e++) |
|
2375 { |
|
2376 const ScissorEdge& se = scissorEdges[e]; |
|
2377 if(dy + j >= se.miny && dy + j < se.maxy) |
|
2378 scissorAet.push_back(scissorEdges[e]); //throws bad_alloc |
|
2379 } |
|
2380 if(!scissorAet.size()) |
|
2381 continue; //scissoring is on, but there are no scissor rectangles on this scanline |
|
2382 |
|
2383 //blit a scanline |
|
2384 int scissorWinding = 0; |
|
2385 int scissorIndex = 0; |
|
2386 for(int i=0;i<w;i++) |
|
2387 { |
|
2388 while(scissorIndex < scissorAet.size() && scissorAet[scissorIndex].x <= dx + i) |
|
2389 scissorWinding += scissorAet[scissorIndex++].direction; |
|
2390 RI_ASSERT(scissorWinding >= 0); |
|
2391 |
|
2392 if(scissorWinding) |
|
2393 { |
|
2394 Color c = src.readPixel(sx + i, sy + j); |
|
2395 c.convert(getDescriptor().internalFormat); |
|
2396 for(int s=0;s<m_numSamples;s++) |
|
2397 writeSample(dx + i, dy + j, s, c); |
|
2398 } |
|
2399 } |
|
2400 } |
|
2401 } |
|
2402 #endif |
|
2403 |
|
2404 /*-------------------------------------------------------------------*//*! |
|
2405 * \brief |
|
2406 * \param |
|
2407 * \return |
|
2408 * \note |
|
2409 *//*-------------------------------------------------------------------*/ |
|
2410 |
|
2411 #if 0 |
|
2412 void Surface::blit(const Surface* src, int sx, int sy, int dx, int dy, int w, int h) |
|
2413 { |
|
2414 Rectangle rect; |
|
2415 rect.x = 0; |
|
2416 rect.y = 0; |
|
2417 rect.width = getWidth(); |
|
2418 rect.height = getHeight(); |
|
2419 Array<Rectangle> scissors; |
|
2420 scissors.push_back(rect); |
|
2421 blit(src, sx, sy, dx, dy, w, h, scissors); |
|
2422 } |
|
2423 #endif |
|
2424 |
|
2425 /*-------------------------------------------------------------------*//*! |
|
2426 * \brief |
|
2427 * \param |
|
2428 * \return |
|
2429 * \note |
|
2430 *//*-------------------------------------------------------------------*/ |
|
2431 |
|
2432 #if 0 |
|
2433 void Surface::blit(const Surface* src, int sx, int sy, int dx, int dy, int w, int h, const Array<Rectangle>& scissors) |
|
2434 { |
|
2435 RI_ASSERT(m_numSamples == src->m_numSamples); |
|
2436 |
|
2437 //fb=>fb: vgCopyPixels |
|
2438 computeBlitRegion(sx, sy, dx, dy, w, h, src->getWidth(), src->getHeight(), getWidth(), getHeight()); |
|
2439 if(w <= 0 || h <= 0) |
|
2440 return; //zero area |
|
2441 |
|
2442 Array<ScissorEdge> scissorEdges; |
|
2443 for(int i=0;i<scissors.size();i++) |
|
2444 { |
|
2445 if(scissors[i].width > 0 && scissors[i].height > 0) |
|
2446 { |
|
2447 ScissorEdge e; |
|
2448 e.miny = scissors[i].y; |
|
2449 e.maxy = RI_INT_ADDSATURATE(scissors[i].y, scissors[i].height); |
|
2450 |
|
2451 e.x = scissors[i].x; |
|
2452 e.direction = 1; |
|
2453 scissorEdges.push_back(e); //throws bad_alloc |
|
2454 e.x = RI_INT_ADDSATURATE(scissors[i].x, scissors[i].width); |
|
2455 e.direction = -1; |
|
2456 scissorEdges.push_back(e); //throws bad_alloc |
|
2457 } |
|
2458 } |
|
2459 if(!scissorEdges.size()) |
|
2460 return; //there are no scissor rectangles => nothing is visible |
|
2461 |
|
2462 //sort scissor edges by edge x |
|
2463 scissorEdges.sort(); |
|
2464 |
|
2465 Array<Color> tmp; |
|
2466 tmp.resize(w*m_numSamples*h); //throws bad_alloc |
|
2467 |
|
2468 //copy source region to tmp |
|
2469 for(int j=0;j<h;j++) |
|
2470 { |
|
2471 for(int i=0;i<w;i++) |
|
2472 { |
|
2473 int numSamples = m_numSamples; |
|
2474 for(int s=0;s<numSamples;s++) |
|
2475 { |
|
2476 Color c = src->m_image->readPixel((sx + i)*m_numSamples+s, sy + j); |
|
2477 c.convert(m_image->getDescriptor().internalFormat); |
|
2478 tmp[(j*w+i)*m_numSamples+s] = c; |
|
2479 } |
|
2480 } |
|
2481 } |
|
2482 |
|
2483 Array<ScissorEdge> scissorAet; |
|
2484 for(int j=0;j<h;j++) |
|
2485 { |
|
2486 //gather scissor edges intersecting this scanline |
|
2487 scissorAet.clear(); |
|
2488 for(int e=0;e<scissorEdges.size();e++) |
|
2489 { |
|
2490 const ScissorEdge& se = scissorEdges[e]; |
|
2491 if(dy + j >= se.miny && dy + j < se.maxy) |
|
2492 scissorAet.push_back(scissorEdges[e]); //throws bad_alloc |
|
2493 } |
|
2494 if(!scissorAet.size()) |
|
2495 continue; //scissoring is on, but there are no scissor rectangles on this scanline |
|
2496 |
|
2497 //blit a scanline |
|
2498 int scissorWinding = 0; |
|
2499 int scissorIndex = 0; |
|
2500 for(int i=0;i<w;i++) |
|
2501 { |
|
2502 while(scissorIndex < scissorAet.size() && scissorAet[scissorIndex].x <= dx + i) |
|
2503 scissorWinding += scissorAet[scissorIndex++].direction; |
|
2504 RI_ASSERT(scissorWinding >= 0); |
|
2505 |
|
2506 if(scissorWinding) |
|
2507 { |
|
2508 int numSamples = m_numSamples; |
|
2509 for(int s=0;s<numSamples;s++) |
|
2510 { |
|
2511 m_image->writePixel((dx + i)*m_numSamples+s, dy + j, tmp[(j*w+i)*m_numSamples+s]); |
|
2512 } |
|
2513 } |
|
2514 } |
|
2515 } |
|
2516 } |
|
2517 #endif |
|
2518 |
|
2519 /*-------------------------------------------------------------------*//*! |
|
2520 * \brief |
|
2521 * \param |
|
2522 * \return |
|
2523 * \note |
|
2524 *//*-------------------------------------------------------------------*/ |
|
2525 |
|
2526 void Surface::mask(DynamicBlitter& blitter, const Image* src, VGMaskOperation operation, int x, int y, int w, int h) |
|
2527 { |
|
2528 RI_ASSERT(w > 0 && h > 0); |
|
2529 RI_ASSERT(m_numSamples == 1); |
|
2530 |
|
2531 if(operation == VG_CLEAR_MASK || operation == VG_FILL_MASK) |
|
2532 { |
|
2533 //intersect clear region with image bounds |
|
2534 Rectangle r(0,0,getWidth(),getHeight()); |
|
2535 r.intersect(Rectangle(x,y,w,h)); |
|
2536 |
|
2537 if(!r.width || !r.height) |
|
2538 return; //intersection is empty or one of the rectangles is invalid |
|
2539 |
|
2540 { |
|
2541 Color mcolor(1.0f, 1.0f, 1.0f, 1.0f, Color::sRGBA_PRE); |
|
2542 if (operation == VG_CLEAR_MASK) |
|
2543 mcolor = Color(0,0,0,0, Color::sRGBA_PRE); |
|
2544 IntegerColor ic = IntegerColor(mcolor); |
|
2545 RIuint32 p = ic.getPackedMaskColor(m_image->getDescriptor()); |
|
2546 m_image->fillPackedRectangle(r.x, r.y, r.width, r.height, p); |
|
2547 } |
|
2548 } |
|
2549 else |
|
2550 { |
|
2551 int sx = 0, sy = 0, dx = x, dy = y; |
|
2552 |
|
2553 computeBlitRegion(sx, sy, dx, dy, w, h, src->getWidth(), src->getHeight(), getWidth(), getHeight()); |
|
2554 |
|
2555 if(w <= 0 || h <= 0) |
|
2556 return; //zero area |
|
2557 |
|
2558 blitter.enableMaskOperation(true); |
|
2559 blitter.setMaskOperation(operation); |
|
2560 blitter.prepareBlit(this->m_image, src, sx, sy, dx, dy, w, h); |
|
2561 blitter.blit(); |
|
2562 blitter.enableMaskOperation(false); |
|
2563 #if 0 |
|
2564 RI_ASSERT(src); |
|
2565 |
|
2566 int sx = 0, sy = 0, dx = x, dy = y; |
|
2567 computeBlitRegion(sx, sy, dx, dy, w, h, src->getWidth(), src->getHeight(), getWidth(), getHeight()); |
|
2568 if(w <= 0 || h <= 0) |
|
2569 return; //zero area |
|
2570 |
|
2571 { |
|
2572 for(int j=0;j<h;j++) |
|
2573 { |
|
2574 for(int i=0;i<w;i++) |
|
2575 { |
|
2576 RIfloat amask = src->readMaskPixel(sx + i, sy + j); |
|
2577 if(operation == VG_SET_MASK) |
|
2578 writeMaskCoverage(dx + i, dy + j, amask); |
|
2579 else |
|
2580 { |
|
2581 RIfloat aprev = readMaskCoverage(dx + i, dy + j); |
|
2582 RIfloat anew = 0.0f; |
|
2583 switch(operation) |
|
2584 { |
|
2585 case VG_UNION_MASK: anew = 1.0f - (1.0f - amask)*(1.0f - aprev); break; |
|
2586 case VG_INTERSECT_MASK: anew = amask * aprev; break; |
|
2587 default: anew = aprev * (1.0f - amask); RI_ASSERT(operation == VG_SUBTRACT_MASK); break; |
|
2588 } |
|
2589 writeMaskCoverage(dx + i, dy + j, anew); |
|
2590 } |
|
2591 } |
|
2592 } |
|
2593 } |
|
2594 #endif |
|
2595 } |
|
2596 } |
|
2597 |
|
2598 /*-------------------------------------------------------------------*//*! |
|
2599 * \brief |
|
2600 * \param |
|
2601 * \return |
|
2602 * \note |
|
2603 *//*-------------------------------------------------------------------*/ |
|
2604 |
|
2605 RIfloat Surface::readMaskCoverage(int x, int y) const |
|
2606 { |
|
2607 RI_ASSERT(x >= 0 && x < m_width && y >= 0 && y < m_height); |
|
2608 RI_ASSERT(m_numSamples == 1); |
|
2609 return m_image->readMaskPixel(x, y); |
|
2610 } |
|
2611 |
|
2612 void Surface::writeMaskCoverage(int x, int y, RIfloat m) |
|
2613 { |
|
2614 RI_ASSERT(x >= 0 && x < m_width && y >= 0 && y < m_height); |
|
2615 RI_ASSERT(m_numSamples == 1); |
|
2616 m_image->writeMaskPixel(x, y, m); //TODO support other than alpha formats but don't write to color channels? |
|
2617 } |
|
2618 |
|
2619 unsigned int Surface::readMaskMSAA(int x, int y) const |
|
2620 { |
|
2621 RI_ASSERT(x >= 0 && x < m_width && y >= 0 && y < m_height); |
|
2622 RI_ASSERT(m_numSamples > 1); |
|
2623 unsigned int m = 0; |
|
2624 for(int i=0;i<m_numSamples;i++) |
|
2625 { |
|
2626 if(m_image->readMaskPixel(x*m_numSamples+i, y) > 0.5f) //TODO is this the right formula for converting alpha to bit mask? does it matter? |
|
2627 m |= 1<<i; |
|
2628 } |
|
2629 return m; |
|
2630 } |
|
2631 |
|
2632 void Surface::writeMaskMSAA(int x, int y, unsigned int m) |
|
2633 { |
|
2634 RI_ASSERT(x >= 0 && x < m_width && y >= 0 && y < m_height); |
|
2635 RI_ASSERT(m_numSamples > 1); |
|
2636 for(int i=0;i<m_numSamples;i++) |
|
2637 { |
|
2638 RIfloat a = 0.0f; |
|
2639 if(m & (1<<i)) |
|
2640 a = 1.0f; |
|
2641 m_image->writeMaskPixel(x*m_numSamples+i, y, a); //TODO support other than alpha formats but don't write to color channels? |
|
2642 } |
|
2643 } |
|
2644 |
|
2645 /*-------------------------------------------------------------------*//*! |
|
2646 * \brief |
|
2647 * \param |
|
2648 * \return |
|
2649 * \note |
|
2650 *//*-------------------------------------------------------------------*/ |
|
2651 |
|
2652 Color Surface::FSAAResolve(int x, int y) const |
|
2653 { |
|
2654 if(m_numSamples == 1) |
|
2655 return readSample(x, y, 0); |
|
2656 |
|
2657 Color::InternalFormat aaFormat = getDescriptor().isLuminance() ? Color::lLA_PRE : Color::lRGBA_PRE; //antialias in linear color space |
|
2658 Color r(0.0f, 0.0f, 0.0f, 0.0f, aaFormat); |
|
2659 for(int i=0;i<m_numSamples;i++) |
|
2660 { |
|
2661 Color d = readSample(x, y, i); |
|
2662 d.convert(aaFormat); |
|
2663 r += d; |
|
2664 } |
|
2665 r *= 1.0f/m_numSamples; |
|
2666 return r; |
|
2667 } |
|
2668 |
|
2669 |
|
2670 /** |
|
2671 * \brief Return a resolved sample in packed format. |
|
2672 * \note Further operations on color may require unpack. |
|
2673 */ |
|
2674 RI_INLINE RIuint32 Surface::FSAAResolvePacked(int x, int y) const |
|
2675 { |
|
2676 if (m_numSamples == 1) |
|
2677 return readPackedSample(x, y, 0); |
|
2678 |
|
2679 RI_ASSERT(false); /* Not implemented yet. */ |
|
2680 return 0xffffffffu; |
|
2681 } |
|
2682 |
|
2683 /*-------------------------------------------------------------------*//*! |
|
2684 * \brief |
|
2685 * \param |
|
2686 * \return |
|
2687 * \note |
|
2688 *//*-------------------------------------------------------------------*/ |
|
2689 |
|
2690 Drawable::Drawable(const Color::Descriptor& desc, int width, int height, int numSamples, int maskBits) : |
|
2691 m_referenceCount(0), |
|
2692 m_color(NULL), |
|
2693 m_mask(NULL) |
|
2694 { |
|
2695 RI_ASSERT(width > 0 && height > 0 && numSamples > 0 && numSamples <= 32); |
|
2696 RI_ASSERT(maskBits == 0 || maskBits == 1 || maskBits == 4 || maskBits == 8); |
|
2697 m_color = RI_NEW(Surface, (desc, width, height, numSamples)); //throws bad_alloc |
|
2698 m_color->addReference(); |
|
2699 if(maskBits) |
|
2700 { |
|
2701 VGImageFormat mf = VG_A_1; |
|
2702 if(maskBits == 4) |
|
2703 mf = VG_A_4; |
|
2704 else if(maskBits == 8) |
|
2705 mf = VG_A_8; |
|
2706 m_mask = RI_NEW(Surface, (Color::formatToDescriptor(mf), width, height, numSamples)); |
|
2707 m_mask->addReference(); |
|
2708 m_mask->clear(Color(1,1,1,1,Color::sRGBA), 0, 0, width, height); |
|
2709 } |
|
2710 } |
|
2711 |
|
2712 /*-------------------------------------------------------------------*//*! |
|
2713 * \brief |
|
2714 * \param |
|
2715 * \return |
|
2716 * \note |
|
2717 *//*-------------------------------------------------------------------*/ |
|
2718 |
|
2719 Drawable::Drawable(Image* image, int maskBits) : |
|
2720 m_referenceCount(0), |
|
2721 m_color(NULL), |
|
2722 m_mask(NULL) |
|
2723 { |
|
2724 RI_ASSERT(maskBits == 0 || maskBits == 1 || maskBits == 4 || maskBits == 8); |
|
2725 RI_ASSERT(image); |
|
2726 m_color = RI_NEW(Surface, (image)); |
|
2727 m_color->addReference(); |
|
2728 if(maskBits) |
|
2729 { |
|
2730 VGImageFormat mf = VG_A_1; |
|
2731 if(maskBits == 4) |
|
2732 mf = VG_A_4; |
|
2733 else if(maskBits == 8) |
|
2734 mf = VG_A_8; |
|
2735 m_mask = RI_NEW(Surface, (Color::formatToDescriptor(mf), image->getWidth(), image->getHeight(), 1)); |
|
2736 m_mask->addReference(); |
|
2737 m_mask->clear(Color(1,1,1,1,Color::sRGBA), 0, 0, image->getWidth(), image->getHeight()); |
|
2738 } |
|
2739 } |
|
2740 |
|
2741 /*-------------------------------------------------------------------*//*! |
|
2742 * \brief |
|
2743 * \param |
|
2744 * \return |
|
2745 * \note |
|
2746 *//*-------------------------------------------------------------------*/ |
|
2747 |
|
2748 Drawable::Drawable(const Color::Descriptor& desc, int width, int height, int stride, RIuint8* data, int maskBits) : |
|
2749 m_referenceCount(0), |
|
2750 m_color(NULL), |
|
2751 m_mask(NULL) |
|
2752 { |
|
2753 RI_ASSERT(width > 0 && height > 0); |
|
2754 RI_ASSERT(maskBits == 0 || maskBits == 1 || maskBits == 4 || maskBits == 8); |
|
2755 m_color = RI_NEW(Surface, (desc, width, height, stride, data)); //throws bad_alloc |
|
2756 m_color->addReference(); |
|
2757 if(maskBits) |
|
2758 { |
|
2759 VGImageFormat mf = VG_A_1; |
|
2760 if(maskBits == 4) |
|
2761 mf = VG_A_4; |
|
2762 else if(maskBits == 8) |
|
2763 mf = VG_A_8; |
|
2764 m_mask = RI_NEW(Surface, (Color::formatToDescriptor(mf), width, height, 1)); |
|
2765 m_mask->addReference(); |
|
2766 m_mask->clear(Color(1,1,1,1,Color::sRGBA), 0, 0, width, height); |
|
2767 } |
|
2768 } |
|
2769 |
|
2770 /*-------------------------------------------------------------------*//*! |
|
2771 * \brief |
|
2772 * \param |
|
2773 * \return |
|
2774 * \note |
|
2775 *//*-------------------------------------------------------------------*/ |
|
2776 |
|
2777 Drawable::~Drawable() |
|
2778 { |
|
2779 RI_ASSERT(m_referenceCount == 0); |
|
2780 if(!m_color->removeReference()) |
|
2781 RI_DELETE(m_color); |
|
2782 if(m_mask) |
|
2783 if(!m_mask->removeReference()) |
|
2784 RI_DELETE(m_mask); |
|
2785 } |
|
2786 |
|
2787 /*-------------------------------------------------------------------*//*! |
|
2788 * \brief |
|
2789 * \param |
|
2790 * \return |
|
2791 * \note |
|
2792 *//*-------------------------------------------------------------------*/ |
|
2793 |
|
2794 void Drawable::resize(VGContext* context, int newWidth, int newHeight) |
|
2795 { |
|
2796 Surface* oldcolor = m_color; |
|
2797 Surface* oldmask = m_mask; |
|
2798 int oldWidth = m_color->getWidth(); |
|
2799 int oldHeight = m_color->getHeight(); |
|
2800 |
|
2801 //TODO check that image is not a proxy |
|
2802 m_color = RI_NEW(Surface, (m_color->getDescriptor(), newWidth, newHeight, m_color->getNumSamples())); |
|
2803 m_color->addReference(); |
|
2804 if(m_mask) |
|
2805 { |
|
2806 m_mask = RI_NEW(Surface, (m_mask->getDescriptor(), newWidth, newHeight, m_mask->getNumSamples())); |
|
2807 m_mask->addReference(); |
|
2808 } |
|
2809 |
|
2810 int wmin = RI_INT_MIN(newWidth,oldWidth); |
|
2811 int hmin = RI_INT_MIN(newHeight,oldHeight); |
|
2812 m_color->clear(Color(0.0f, 0.0f, 0.0f, 0.0f, getDescriptor().internalFormat), 0, 0, m_color->getWidth(), m_color->getHeight()); |
|
2813 m_color->m_image->blit(context, oldcolor->m_image, 0, 0, 0, 0, wmin, hmin); |
|
2814 if(m_mask) |
|
2815 { |
|
2816 m_mask->clear(Color(1.0f, 1.0f, 1.0f, 1.0f, getDescriptor().internalFormat), 0, 0, m_mask->getWidth(), m_mask->getHeight()); |
|
2817 m_mask->m_image->blit(context, oldmask->m_image, 0, 0, 0, 0, wmin, hmin); |
|
2818 } |
|
2819 |
|
2820 if(!oldcolor->removeReference()) |
|
2821 RI_DELETE(oldcolor); |
|
2822 if(oldmask) |
|
2823 if(!oldmask->removeReference()) |
|
2824 RI_DELETE(oldmask); |
|
2825 } |
|
2826 |
|
2827 #ifndef RI_COMPILE_LLVM_BYTECODE |
|
2828 |
|
2829 #endif /* RI_COMPILE_LLVM_BYTECODE */ |
|
2830 |
|
2831 //============================================================================================== |
|
2832 |
|
2833 } //namespace OpenVGRI |
|
2834 |
|
2835 //============================================================================================== |