|
1 /* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
2 * |
|
3 * Permission is hereby granted, free of charge, to any person obtaining a |
|
4 * copy of this software and /or associated documentation files |
|
5 * (the "Materials "), to deal in the Materials without restriction, |
|
6 * including without limitation the rights to use, copy, modify, merge, |
|
7 * publish, distribute, sublicense, and/or sell copies of the Materials, |
|
8 * and to permit persons to whom the Materials are furnished to do so, |
|
9 * subject to the following conditions: |
|
10 * |
|
11 * The above copyright notice and this permission notice shall be included |
|
12 * in all copies or substantial portions of the Materials. |
|
13 * |
|
14 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
|
18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
19 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR |
|
20 * THE USE OR OTHER DEALINGS IN THE MATERIALS. |
|
21 */ |
|
22 |
|
23 // This file contains the generated pixel-pipeline code and provides |
|
24 // interface to compile and run them. |
|
25 |
|
26 #ifndef __RIRASTERIZER_H |
|
27 # include "riRasterizer.h" |
|
28 #endif |
|
29 |
|
30 #ifndef __RIPIXELPIPE_H |
|
31 # include "riPixelPipe.h" |
|
32 #endif |
|
33 |
|
34 #ifndef __SFDYNAMICPIXELPIPE_H |
|
35 # include "sfDynamicPixelPipe.h" |
|
36 #endif |
|
37 |
|
38 #ifndef __RIUTILS_H |
|
39 # include "riUtils.h" |
|
40 #endif |
|
41 |
|
42 #ifndef __SFMASK_H |
|
43 # include "sfMask.h" |
|
44 #endif |
|
45 |
|
46 #ifndef __RIIMAGE_H |
|
47 # include "riImage.h" |
|
48 #endif |
|
49 |
|
50 #if defined(RI_DEBUG) |
|
51 # include <stdio.h> |
|
52 #endif |
|
53 |
|
54 namespace OpenVGRI { |
|
55 |
|
56 RI_INLINE static bool alwaysLoadDst(const PixelPipe::SignatureState& state) |
|
57 { |
|
58 if (!state.isRenderToMask) |
|
59 { |
|
60 if (state.hasImage) |
|
61 return true; |
|
62 |
|
63 VGBlendMode bm = state.blendMode; |
|
64 |
|
65 if (bm == VG_BLEND_SRC_IN || |
|
66 bm == VG_BLEND_DST_OVER || |
|
67 bm == VG_BLEND_DST_IN || |
|
68 bm == VG_BLEND_ADDITIVE || |
|
69 bm == VG_BLEND_MULTIPLY || |
|
70 bm == VG_BLEND_SCREEN || |
|
71 bm == VG_BLEND_DARKEN || |
|
72 bm == VG_BLEND_LIGHTEN) |
|
73 { |
|
74 return true; |
|
75 } else |
|
76 { |
|
77 return false; |
|
78 } |
|
79 } |
|
80 else |
|
81 { |
|
82 switch (state.maskOperation) |
|
83 { |
|
84 case VG_SET_MASK: |
|
85 return false; |
|
86 default: |
|
87 return true; |
|
88 } |
|
89 } |
|
90 } |
|
91 |
|
92 RI_INLINE static bool canSolidFill(const PixelPipe::SignatureState& state) |
|
93 { |
|
94 if (state.isRenderToMask) |
|
95 { |
|
96 if (state.maskOperation == VG_SET_MASK || |
|
97 state.maskOperation == VG_UNION_MASK) |
|
98 return true; |
|
99 // \note SUBTRACT is also possible. |
|
100 return false; |
|
101 } |
|
102 |
|
103 if (state.paintType != VG_PAINT_TYPE_COLOR) |
|
104 return false; |
|
105 |
|
106 if (state.hasImage) |
|
107 return false; |
|
108 |
|
109 // Some blendmodes can use dst color even if coverage == 1.0 |
|
110 if (state.blendMode != VG_BLEND_SRC && state.blendMode != VG_BLEND_SRC_OVER) |
|
111 return false; |
|
112 |
|
113 if (state.hasMasking) |
|
114 return false; |
|
115 |
|
116 if (state.fillColorTransparent) |
|
117 return false; |
|
118 |
|
119 if (state.hasColorTransform) |
|
120 return false; // \todo Trace solid color alpha -> 1.0 |
|
121 |
|
122 return true; |
|
123 } |
|
124 |
|
125 RI_INLINE static int intReflectRepeat(int n, int bits) |
|
126 { |
|
127 const int mask = (1<<bits)-1; |
|
128 return (n ^ (n << (31 - bits) >> 31)) & mask; |
|
129 } |
|
130 |
|
131 RI_INLINE static void applyGradientRepeat(int& sx0, int& sx1, PixelPipe::TilingMode sm) |
|
132 { |
|
133 switch (sm) |
|
134 { |
|
135 case PixelPipe::TILING_MODE_PAD: |
|
136 sx0 = RI_INT_CLAMP(sx0, 0, PixelPipe::SAMPLE_MASK); |
|
137 sx1 = RI_INT_CLAMP(sx1, 0, PixelPipe::SAMPLE_MASK); |
|
138 break; |
|
139 case PixelPipe::TILING_MODE_REFLECT: |
|
140 sx0 = intReflectRepeat(sx0, PixelPipe::SAMPLE_BITS); |
|
141 sx1 = intReflectRepeat(sx1, PixelPipe::SAMPLE_BITS); |
|
142 break; |
|
143 default: |
|
144 RI_ASSERT(sm == PixelPipe::TILING_MODE_REPEAT); |
|
145 |
|
146 sx0 = sx0 & PixelPipe::SAMPLE_MASK; |
|
147 sx1 = sx1 & PixelPipe::SAMPLE_MASK; |
|
148 break; |
|
149 } |
|
150 |
|
151 RI_ASSERT(sx0 >= 0 && sx0 < (1<<Paint::GRADIENT_LUT_BITS)); |
|
152 RI_ASSERT(sx1 >= 0 && sx1 < (1<<Paint::GRADIENT_LUT_BITS)); |
|
153 |
|
154 } |
|
155 |
|
156 RI_INLINE static IntegerColor readLUTColor(const PixelPipe::PPUniforms& uniforms, int i) |
|
157 { |
|
158 RI_ASSERT(i >= 0 && i <= Paint::GRADIENT_LUT_MASK); |
|
159 return uniforms.gradientLookup[i]; |
|
160 } |
|
161 |
|
162 |
|
163 /** |
|
164 * \brief Sample linear gradient using integer-arithmetic. |
|
165 * \note The actual gradient computation is done piecewise within the |
|
166 * pixel-pipeline. |
|
167 */ |
|
168 RI_INLINE static IntegerColor intLinearGradient(const PixelPipe::SignatureState& state, const PixelPipe::PPUniforms& u, const PixelPipe::PPVariants& v) |
|
169 { |
|
170 RIint32 sx0 = v.sx >> (PixelPipe::GRADIENT_BITS - PixelPipe::SAMPLE_BITS); |
|
171 RIint32 sx1 = sx0 + 1; |
|
172 |
|
173 applyGradientRepeat(sx0, sx1, state.paintTilingMode); |
|
174 |
|
175 IntegerColor ic0 = readLUTColor(u, sx0 >> (PixelPipe::SAMPLE_BITS - Paint::GRADIENT_LUT_BITS)); |
|
176 |
|
177 if (true) |
|
178 { |
|
179 return ic0; |
|
180 } else |
|
181 { |
|
182 // bilinear interpolation |
|
183 //RIint32 f1 = sx0; |
|
184 readLUTColor(u, sx1 >> (PixelPipe::SAMPLE_BITS - Paint::GRADIENT_LUT_BITS)); |
|
185 RI_ASSERT(false); |
|
186 return IntegerColor(0,0,0,0); |
|
187 } |
|
188 } |
|
189 |
|
190 /** |
|
191 * \brief Radial gradient implementation for the integer-pipeline. Will use float at least |
|
192 * for the square-root. Will return integer-color always. |
|
193 */ |
|
194 RI_INLINE static IntegerColor intRadialGradient(const PixelPipe::SignatureState& state, const PixelPipe::PPUniforms& u, const PixelPipe::PPVariants& v) |
|
195 { |
|
196 RGScalar a = (v.rx * u.rfxp) + (v.ry * u.rfyp); |
|
197 RGScalar b = u.rsqrp * (RI_SQR(v.rx) + RI_SQR(v.ry)); |
|
198 RGScalar c = RI_SQR((v.rx * u.rfyp) - (v.ry * u.rfxp)); |
|
199 RGScalar d = b - c; |
|
200 RI_ASSERT(!RI_ISNAN(d) ? d >= 0.0f : true); |
|
201 RGScalar g = (a + sqrtf(d)); |
|
202 |
|
203 int sx0 = RI_FLOAT_TO_FX(g, PixelPipe::SAMPLE_BITS); |
|
204 int sx1 = sx0 + 1; |
|
205 |
|
206 applyGradientRepeat(sx0, sx1, state.paintTilingMode); |
|
207 |
|
208 IntegerColor ic0 = readLUTColor(u, sx0 >> (PixelPipe::SAMPLE_BITS - Paint::GRADIENT_LUT_BITS)); |
|
209 RI_ASSERT(ic0.r <= 255); |
|
210 RI_ASSERT(ic0.g <= 255); |
|
211 RI_ASSERT(ic0.b <= 255); |
|
212 RI_ASSERT(ic0.a <= 255); |
|
213 |
|
214 if (false) |
|
215 { |
|
216 // Linear interpolation of 2 gradient samples. |
|
217 IntegerColor ic1 = readLUTColor(u, sx1 >> (PixelPipe::SAMPLE_BITS - Paint::GRADIENT_LUT_BITS)); |
|
218 //int fx0 = sx0 & PixelPipe::SAMPLE_MASK; |
|
219 //int fx1 = PixelPipe::SAMPLE_MASK - fx0; |
|
220 |
|
221 } |
|
222 |
|
223 return ic0; |
|
224 } |
|
225 |
|
226 RI_INLINE static bool applyPatternRepeat(int &x, int &y, PixelPipe::TilingMode tilingMode) |
|
227 { |
|
228 switch (tilingMode) |
|
229 { |
|
230 case PixelPipe::TILING_MODE_PAD: |
|
231 x = RI_INT_CLAMP(x, 0, PixelPipe::GRADIENT_MASK); |
|
232 y = RI_INT_CLAMP(y, 0, PixelPipe::GRADIENT_MASK); |
|
233 break; |
|
234 case PixelPipe::TILING_MODE_REPEAT: |
|
235 x = x & PixelPipe::GRADIENT_MASK; |
|
236 y = y & PixelPipe::GRADIENT_MASK; |
|
237 break; |
|
238 case PixelPipe::TILING_MODE_REFLECT: |
|
239 x = intReflectRepeat(x, PixelPipe::GRADIENT_BITS); |
|
240 y = intReflectRepeat(y, PixelPipe::GRADIENT_BITS); |
|
241 break; |
|
242 default: |
|
243 RI_ASSERT(tilingMode == PixelPipe::TILING_MODE_FILL); |
|
244 // Do nothing -> Fill is checked on integer coordinates. |
|
245 break; |
|
246 } |
|
247 return false; |
|
248 } |
|
249 |
|
250 /** |
|
251 * \brief Same as applyPatternRepeat, but with pattern-space integer coordinates without |
|
252 * fractional part. |
|
253 * \note Assumes that the coordinate is in range [0,width or height]. |
|
254 */ |
|
255 RI_INLINE static bool applyPatternSampleRepeat(int &x, int &y, int w, int h, PixelPipe::TilingMode tilingMode) |
|
256 { |
|
257 |
|
258 switch (tilingMode) |
|
259 { |
|
260 case PixelPipe::TILING_MODE_PAD: |
|
261 RI_ASSERT(x >= 0 && x <= w); |
|
262 RI_ASSERT(y >= 0 && y <= h); |
|
263 if (x >= w) x = w-1; |
|
264 if (y >= h) y = h-1; |
|
265 break; |
|
266 case PixelPipe::TILING_MODE_REPEAT: |
|
267 RI_ASSERT(x >= 0 && x <= w); |
|
268 RI_ASSERT(y >= 0 && y <= h); |
|
269 if (x >= w) x = 0; |
|
270 if (y >= h) y = 0; |
|
271 break; |
|
272 case PixelPipe::TILING_MODE_REFLECT: |
|
273 RI_ASSERT(x >= 0 && x <= w); |
|
274 RI_ASSERT(y >= 0 && y <= h); |
|
275 if (x >= w) x = w-1; // w-2? |
|
276 if (y >= h) y = h-1; // h-2? |
|
277 break; |
|
278 default: |
|
279 RI_ASSERT(tilingMode == PixelPipe::TILING_MODE_FILL); |
|
280 if (x < 0 || x >= w) return true; |
|
281 if (y < 0 || y >= h) return true; |
|
282 break; |
|
283 } |
|
284 |
|
285 return false; |
|
286 } |
|
287 |
|
288 RI_INLINE IntegerColor readPattern(const void* basePtr, int stride, const Color::Descriptor& desc, int ix, int iy, const IntegerColor* fillColor, bool fill) |
|
289 { |
|
290 const void* ptr = Image::calculateAddress(basePtr, desc.bitsPerPixel, ix, iy, stride); |
|
291 |
|
292 if (!fill) |
|
293 return IntegerColor(Image::readPackedPixelFromAddress(ptr, desc.bitsPerPixel, ix), desc); |
|
294 else |
|
295 { |
|
296 RI_ASSERT(fillColor); |
|
297 return *fillColor; |
|
298 } |
|
299 |
|
300 } |
|
301 |
|
302 /** |
|
303 * \brief Rescale the result of bilinear interpolation. |
|
304 * \todo See if this or individual shifts and rounds are faster on x86 |
|
305 */ |
|
306 RI_INLINE static RIuint32 bilinearDiv(unsigned int c) |
|
307 { |
|
308 RIuint32 rcp = 33026; |
|
309 |
|
310 RIuint64 m = (RIuint64)c * rcp; |
|
311 RIuint32 d = (RIuint32)(m >> 30); |
|
312 return (d >> 1) + (d & 1); |
|
313 } |
|
314 |
|
315 /** |
|
316 * \brief Read an optionally filtered sample from an image. For multiple samples, apply repeat |
|
317 * for all the generated sampling points. This only implements a simple sampling: nearest |
|
318 * or Linear filtering and is much simpler than the original RI. |
|
319 * \param image Image to sample from |
|
320 * \param sx0 Sample x in .8 fixed point. MUST be within the image except for FILL. |
|
321 * \param sy0 Sample y in .8 fixed point. MUST be within the image except for FILL. |
|
322 * \param samplerType Type of the sampler used. |
|
323 * \param tilingMode Tiling mode for generated sample points, if required. |
|
324 * \param fillColor Color to use for TILING_MODE_FILL |
|
325 * \todo Where should we determine if a NN-sample needs to be unpacked? |
|
326 * -> It is also easy to just read that sample separately. |
|
327 */ |
|
328 RI_INLINE static IntegerColor intSampleImage( |
|
329 const void* ptr, |
|
330 int stride, |
|
331 int w, |
|
332 int h, |
|
333 const Color::Descriptor& desc, |
|
334 RIint32 sx0, |
|
335 RIint32 sy0, |
|
336 PixelPipe::SamplerType samplerType, |
|
337 PixelPipe::TilingMode tilingMode, |
|
338 const IntegerColor* fillColor) |
|
339 { |
|
340 RI_ASSERT(fillColor || (tilingMode != PixelPipe::TILING_MODE_FILL)); |
|
341 |
|
342 // \todo The following code is between low- and high-level representation of sampling. |
|
343 // It should probably be modified to appear fully as low-level, since we want as many |
|
344 // optimizations as possible. |
|
345 |
|
346 const bool bilinear = samplerType == PixelPipe::SAMPLER_TYPE_LINEAR; |
|
347 |
|
348 IntegerColor retColor; |
|
349 bool maybeFill = tilingMode == PixelPipe::TILING_MODE_FILL; |
|
350 bool fillSample = false; |
|
351 |
|
352 RIint32 ix, iy; |
|
353 |
|
354 IntegerColor ic00; |
|
355 |
|
356 RIint32 fx = sx0 & 0xff; |
|
357 RIint32 fy = sy0 & 0xff; |
|
358 |
|
359 ix = sx0 >> PixelPipe::SAMPLE_BITS; |
|
360 iy = sy0 >> PixelPipe::SAMPLE_BITS; |
|
361 |
|
362 if (maybeFill) |
|
363 { |
|
364 if (ix < 0 || ix >= w) |
|
365 fillSample = true; |
|
366 if (iy < 0 || iy >= h) |
|
367 fillSample = true; |
|
368 } |
|
369 |
|
370 ic00 = readPattern(ptr, stride, desc, ix, iy, fillColor, fillSample); |
|
371 |
|
372 if (!bilinear) |
|
373 { |
|
374 retColor = ic00; |
|
375 retColor.expandColor(desc); // \todo Handling of bilinear? |
|
376 } |
|
377 else |
|
378 { |
|
379 // Bilinear filtering. |
|
380 |
|
381 IntegerColor ic01, ic10, ic11; |
|
382 IntegerColor t0, t1; |
|
383 |
|
384 int xs = ix + 1; |
|
385 int ys = iy; |
|
386 |
|
387 fillSample = applyPatternSampleRepeat(xs, ys, w, h, tilingMode); |
|
388 ic01 = readPattern(ptr, stride, desc, xs, ys, fillColor, fillSample); |
|
389 |
|
390 t0 = IntegerColor::linearBlendNS(ic00, ic01, fx); |
|
391 |
|
392 xs = ix; |
|
393 ys = iy+1; |
|
394 fillSample = applyPatternSampleRepeat(xs, ys, w, h, tilingMode); |
|
395 ic10 = readPattern(ptr, stride, desc, xs, ys, fillColor, fillSample); |
|
396 |
|
397 xs = ix+1; |
|
398 ys = iy+1; |
|
399 fillSample = applyPatternSampleRepeat(xs, ys, w, h, tilingMode); |
|
400 ic11 = readPattern(ptr, stride, desc, xs, ys, fillColor, fillSample); |
|
401 |
|
402 t1 = IntegerColor::linearBlendNS(ic10, ic11, fx); |
|
403 |
|
404 retColor = IntegerColor::linearBlendNS(t0, t1, fy); |
|
405 |
|
406 retColor.r = bilinearDiv(retColor.r); |
|
407 retColor.g = bilinearDiv(retColor.g); |
|
408 retColor.b = bilinearDiv(retColor.b); |
|
409 retColor.a = bilinearDiv(retColor.a); |
|
410 |
|
411 return retColor; |
|
412 } |
|
413 |
|
414 return retColor; |
|
415 } |
|
416 |
|
417 RI_INLINE static RIint32 gradientToFixedCoords(RIint32 gradCoord, RIint32 dim) |
|
418 { |
|
419 return (RIint32)(((RIint64)dim * gradCoord) >> (PixelPipe::GRADIENT_BITS - PixelPipe::SAMPLE_BITS)); |
|
420 } |
|
421 |
|
422 |
|
423 RI_INLINE static IntegerColor intPattern(const PixelPipe::SignatureState &state, const PixelPipe::PPUniforms& u, const PixelPipe::PPVariants& v) |
|
424 { |
|
425 // \todo The following code is between low- and high-level representation of sampling. |
|
426 // It should probably be modified to appear fully as low-level, since we want as many |
|
427 // optimizations as possible. |
|
428 |
|
429 // "External" variables |
|
430 const PixelPipe::TilingMode tilingMode = state.paintTilingMode; |
|
431 const IntegerColor fillColor = u.tileFillColor; |
|
432 const int w = u.paint_width; |
|
433 const int h = u.paint_height; |
|
434 |
|
435 IntegerColor retColor; |
|
436 |
|
437 RIint32 sx0 = v.sx; |
|
438 RIint32 sy0 = v.sy; |
|
439 |
|
440 IntegerColor ic00; |
|
441 |
|
442 applyPatternRepeat(sx0, sy0, tilingMode); |
|
443 sx0 = gradientToFixedCoords(sx0, w); |
|
444 sy0 = gradientToFixedCoords(sy0, h); |
|
445 //sx0 = (RIint32)(((RIint64)w * sx0) >> (PixelPipe::GRADIENT_BITS - PixelPipe::SAMPLE_BITS)); |
|
446 //sy0 = (RIint32)(((RIint64)h * sy0) >> (PixelPipe::GRADIENT_BITS - PixelPipe::SAMPLE_BITS)); |
|
447 |
|
448 const void* ptr = u.patternPtr; |
|
449 const int stride = u.patternStride; |
|
450 const Color::Descriptor& desc = state.patternDesc; |
|
451 |
|
452 return intSampleImage(ptr, stride, w, h, desc, sx0, sy0, state.paintSampler, tilingMode, &fillColor); |
|
453 } |
|
454 |
|
455 RI_INLINE static bool formatPremultipliedAfterSampling(const Color::Descriptor& desc, PixelPipe::SamplerType samplerType, PixelPipe::ImageGradientType gradientType) |
|
456 { |
|
457 // Sampled at pixel centers -> no processing of colors -> does not get premultiplied |
|
458 if (gradientType == PixelPipe::GRADIENT_TYPE_INTEGER) |
|
459 return desc.isPremultiplied(); |
|
460 |
|
461 if (samplerType != PixelPipe::SAMPLER_TYPE_NEAREST) |
|
462 return true; |
|
463 |
|
464 return desc.isPremultiplied(); |
|
465 } |
|
466 |
|
467 RI_INLINE static bool imagePremultipliedAfterSampling(const PixelPipe::SignatureState& state) |
|
468 { |
|
469 RI_ASSERT(state.hasImage); |
|
470 |
|
471 return formatPremultipliedAfterSampling(state.imageDesc, state.imageSampler, state.imageGradientType); |
|
472 } |
|
473 |
|
474 RI_INLINE static bool gradientPremultipliedAfterSampling(const PixelPipe::SignatureState& state) |
|
475 { |
|
476 if (state.paintSampler != PixelPipe::SAMPLER_TYPE_NEAREST) |
|
477 return true; |
|
478 |
|
479 return true; |
|
480 |
|
481 // Otherwise, the gradient value is a single sample, and should be in the destination |
|
482 // color-space: |
|
483 //return state.dstDesc.isPremultiplied(); |
|
484 } |
|
485 |
|
486 RI_INLINE static bool patternPremultipliedAfterSampling(const PixelPipe::SignatureState& state) |
|
487 { |
|
488 RI_ASSERT(state.paintType == VG_PAINT_TYPE_PATTERN); |
|
489 |
|
490 return formatPremultipliedAfterSampling(state.patternDesc, state.paintSampler, PixelPipe::GRADIENT_TYPE_FIXED); |
|
491 } |
|
492 |
|
493 /** |
|
494 * \brief Returns true if generated paint will be in RGB, false if luminance. |
|
495 */ |
|
496 RI_INLINE static bool paintInRGB(const PixelPipe::SignatureState& state) |
|
497 { |
|
498 if (state.paintType != VG_PAINT_TYPE_PATTERN) |
|
499 return true; |
|
500 |
|
501 return !state.patternDesc.isLuminance(); |
|
502 } |
|
503 |
|
504 |
|
505 /** |
|
506 * \brief Applies color transform to input color |
|
507 * \param isNonlinear "true" if input is nonlinear. This only affects luminance -> RGB conversion, |
|
508 * other conversions happen in the input color-space. |
|
509 * \note Leaves the color unpremultiplied, in source color-space and converts luminance to RGB |
|
510 * \todo isNonlinear is not needed. It can be deduced from the state information! |
|
511 */ |
|
512 RI_INLINE static IntegerColor maybeColorTransform(const PixelPipe::SignatureState& state, const IntegerColor& c, const RIint32* colorTransformValues, bool isNonlinear) |
|
513 { |
|
514 if (!state.hasColorTransform) |
|
515 return c; |
|
516 |
|
517 RI_ASSERT(state.hasImage || state.paintType == VG_PAINT_TYPE_PATTERN); |
|
518 |
|
519 IntegerColor r = c; |
|
520 |
|
521 if (state.imageMode == VG_DRAW_IMAGE_MULTIPLY) |
|
522 { |
|
523 r.unpremultiply(); |
|
524 } |
|
525 else if (state.imageMode == VG_DRAW_IMAGE_STENCIL || state.paintType == VG_PAINT_TYPE_PATTERN) |
|
526 { |
|
527 // -> Check pattern |
|
528 if (patternPremultipliedAfterSampling(state)) |
|
529 r.unpremultiply(); |
|
530 } |
|
531 else |
|
532 { |
|
533 // -> Check image |
|
534 if (imagePremultipliedAfterSampling(state)) |
|
535 r.unpremultiply(); |
|
536 } |
|
537 |
|
538 // Check if it is necessary to convert to RGB: |
|
539 if (state.imageMode == VG_DRAW_IMAGE_MULTIPLY) |
|
540 { |
|
541 if (state.imageDesc.isLuminance() && !paintInRGB(state)) |
|
542 { |
|
543 r.fullLuminanceToRGB(false, isNonlinear, false, isNonlinear); |
|
544 } |
|
545 } |
|
546 else if (state.imageMode == VG_DRAW_IMAGE_STENCIL) |
|
547 { |
|
548 if (state.patternDesc.isLuminance()) |
|
549 r.fullLuminanceToRGB(false, isNonlinear, false, isNonlinear); |
|
550 } |
|
551 |
|
552 // \todo Use lookup-tables in some cases? |
|
553 r.r = (((RIint32)r.r * colorTransformValues[0]) >> PixelPipe::COLOR_TRANSFORM_BITS) + colorTransformValues[4]; |
|
554 r.g = (((RIint32)r.g * colorTransformValues[1]) >> PixelPipe::COLOR_TRANSFORM_BITS) + colorTransformValues[5]; |
|
555 r.b = (((RIint32)r.b * colorTransformValues[2]) >> PixelPipe::COLOR_TRANSFORM_BITS) + colorTransformValues[6]; |
|
556 r.a = (((RIint32)r.a * colorTransformValues[3]) >> PixelPipe::COLOR_TRANSFORM_BITS) + colorTransformValues[7]; |
|
557 |
|
558 // Clamp (integerColor?) |
|
559 r.r = (RIuint32)RI_INT_CLAMP((int)r.r, 0, 255); |
|
560 r.g = (RIuint32)RI_INT_CLAMP((int)r.g, 0, 255); |
|
561 r.b = (RIuint32)RI_INT_CLAMP((int)r.b, 0, 255); |
|
562 r.a = (RIuint32)RI_INT_CLAMP((int)r.a, 0, 255); |
|
563 |
|
564 |
|
565 return r; |
|
566 } |
|
567 |
|
568 /// Some rounding multiplications for blends: |
|
569 |
|
570 /** |
|
571 * \brief Multiply with rounding. |
|
572 */ |
|
573 RI_INLINE static RIuint32 rMul2(RIuint32 c0, RIuint32 c1, RIuint32 k0, RIuint32 k1) |
|
574 { |
|
575 RIuint32 t = c0 * k0 + c1 * k1; |
|
576 //RIuint32 r = (t + (t>>9)) >> 8; |
|
577 RIuint32 r = (t + (1>>7))>>8; |
|
578 RI_ASSERT(r <= 255); |
|
579 return r; |
|
580 } |
|
581 |
|
582 /** |
|
583 * \brief Returns rounding color-multiplication: c0 + c1 * k |
|
584 */ |
|
585 RI_INLINE static RIuint32 rMul1(RIuint32 c0, RIuint32 c1, RIuint32 k) |
|
586 { |
|
587 RIuint32 t = c1 * k; |
|
588 RIuint32 r = c0 + ((t + (t >> 7)) >> 8); |
|
589 RI_ASSERT(r <= 255); |
|
590 return r; |
|
591 } |
|
592 |
|
593 /** |
|
594 * \brief Fixed-point multiplication |
|
595 */ |
|
596 RI_INLINE static RIuint32 rMul(RIuint32 c0, RIuint32 f) |
|
597 { |
|
598 RIuint32 t = c0 * f; |
|
599 return (t + (1<<7))>>8; |
|
600 } |
|
601 |
|
602 /** |
|
603 * \brief Multiply two colors [0, 255] |
|
604 */ |
|
605 RI_INLINE static RIuint32 cMul(RIuint32 c0, RIuint32 c1) |
|
606 { |
|
607 RIuint32 t = c0 * c1; |
|
608 RIuint32 r = (t + (t >> 9)) >> 8; |
|
609 //RIuint32 t = c0 * c1; |
|
610 //RIuint32 r = (t + (t >> 7))>>8; |
|
611 RI_ASSERT(r <= 255); |
|
612 return r; |
|
613 } |
|
614 |
|
615 // \todo Are signed versions required? |
|
616 RI_INLINE static RIuint32 cMin(RIuint32 c0, RIuint32 c1) |
|
617 { |
|
618 return c0 <= c1 ? c0 : c1; |
|
619 } |
|
620 |
|
621 RI_INLINE static RIuint32 cMax(RIuint32 c0, RIuint32 c1) |
|
622 { |
|
623 return c0 >= c1 ? c0 : c1; |
|
624 } |
|
625 |
|
626 /** |
|
627 * \brief Blends two integer colors. Only considers the alpha-channels within |
|
628 * the colors themselves. There should be a separate function to do |
|
629 * blending with individual channel-alphas. |
|
630 * \note It is also possible that LLVM is able to detect, whether individual alpha- |
|
631 * channels contain a single/multi alpha |
|
632 * \todo Overall, check how much and how fast LLVM is able to optimize out unused |
|
633 * expressions. |
|
634 */ |
|
635 RI_INLINE static IntegerColor blendIntegerColors(const IntegerColor& s, const IntegerColor& d, VGBlendMode blendMode) |
|
636 { |
|
637 IntegerColor r; |
|
638 |
|
639 switch(blendMode) |
|
640 { |
|
641 case VG_BLEND_SRC: |
|
642 r = s; |
|
643 break; |
|
644 |
|
645 case VG_BLEND_SRC_OVER: |
|
646 { |
|
647 RIuint32 ia = 255 - s.a; |
|
648 r.r = rMul1(s.r, d.r, ia); |
|
649 r.g = rMul1(s.g, d.g, ia); |
|
650 r.b = rMul1(s.b, d.b, ia); |
|
651 r.a = rMul1(s.a, d.a, ia); |
|
652 break; |
|
653 } |
|
654 case VG_BLEND_DST_OVER: |
|
655 { |
|
656 RIuint32 ia = 255 - d.a; |
|
657 r.r = rMul1(d.r, s.r, ia); |
|
658 r.g = rMul1(d.g, s.g, ia); |
|
659 r.b = rMul1(d.b, s.b, ia); |
|
660 r.a = rMul1(d.a, s.a, ia); |
|
661 break; |
|
662 } |
|
663 case VG_BLEND_SRC_IN: |
|
664 { |
|
665 r.r = cMul(s.r, d.a); |
|
666 r.g = cMul(s.g, d.a); |
|
667 r.b = cMul(s.b, d.a); |
|
668 r.a = cMul(s.a, d.a); |
|
669 break; |
|
670 } |
|
671 case VG_BLEND_DST_IN: |
|
672 { |
|
673 r.r = cMul(d.r, s.a); |
|
674 r.g = cMul(d.g, s.a); |
|
675 r.b = cMul(d.b, s.a); |
|
676 r.a = cMul(d.a, s.a); |
|
677 break; |
|
678 } |
|
679 case VG_BLEND_MULTIPLY: |
|
680 { |
|
681 RIuint32 iasrc, iadst; |
|
682 iasrc = 255 - s.a; |
|
683 iadst = 255 - d.a; |
|
684 r.r = rMul2(s.r, d.r, iadst + d.r, iasrc); |
|
685 r.g = rMul2(s.g, d.g, iadst + d.g, iasrc); |
|
686 r.b = rMul2(s.b, d.b, iadst + d.b, iasrc); |
|
687 r.a = rMul1(s.a, d.a, iasrc); |
|
688 break; |
|
689 } |
|
690 case VG_BLEND_SCREEN: |
|
691 { |
|
692 r.r = rMul1(s.r, d.r, 255 - s.r); |
|
693 r.g = rMul1(s.g, d.g, 255 - s.g); |
|
694 r.b = rMul1(s.b, d.b, 255 - s.b); |
|
695 r.a = rMul1(s.a, d.a, 255 - s.a); |
|
696 break; |
|
697 } |
|
698 case VG_BLEND_DARKEN: |
|
699 { |
|
700 RIuint32 iasrc = 255 - s.a; |
|
701 RIuint32 iadst = 255 - d.a; |
|
702 r.r = cMin(rMul1(s.r, d.r, iasrc), rMul1(d.r, s.r, iadst)); |
|
703 r.g = cMin(rMul1(s.g, d.g, iasrc), rMul1(d.g, s.g, iadst)); |
|
704 r.b = cMin(rMul1(s.b, d.b, iasrc), rMul1(d.b, s.b, iadst)); |
|
705 r.a = rMul1(s.a, d.a, iasrc); |
|
706 break; |
|
707 } |
|
708 case VG_BLEND_LIGHTEN: |
|
709 { |
|
710 // \todo Compact darken w/r lighten? |
|
711 RIuint32 iasrc = 255 - s.a; |
|
712 RIuint32 iadst = 255 - d.a; |
|
713 r.r = cMax(rMul1(s.r, d.r, iasrc), rMul1(d.r, s.r, iadst)); |
|
714 r.g = cMax(rMul1(s.g, d.g, iasrc), rMul1(d.g, s.g, iadst)); |
|
715 r.b = cMax(rMul1(s.b, d.b, iasrc), rMul1(d.b, s.b, iadst)); |
|
716 //although the statement below is equivalent to r.a = s.a + d.a * (1.0f - s.a) |
|
717 //in practice there can be a very slight difference because |
|
718 //of the max operation in the blending formula that may cause color to exceed alpha. |
|
719 //Because of this, we compute the result both ways and return the maximum. |
|
720 r.a = cMax(rMul1(s.a, d.a, iasrc), rMul1(d.a, s.a, iadst)); |
|
721 break; |
|
722 } |
|
723 default: |
|
724 { |
|
725 RI_ASSERT(blendMode == VG_BLEND_ADDITIVE); |
|
726 r.r = cMin(s.r + d.r, 255); |
|
727 r.g = cMin(s.g + d.g, 255); |
|
728 r.b = cMin(s.b + d.b, 255); |
|
729 r.a = cMin(s.a + d.a, 255); |
|
730 break; |
|
731 } |
|
732 } |
|
733 return r; |
|
734 |
|
735 } |
|
736 |
|
737 RI_INLINE static IntegerColor blendIntegerStencil(const IntegerColor& s, const IntegerColor& im, const IntegerColor& d, VGBlendMode blendMode) |
|
738 { |
|
739 IntegerColor r; |
|
740 |
|
741 switch(blendMode) |
|
742 { |
|
743 case VG_BLEND_SRC: |
|
744 r = s; |
|
745 break; |
|
746 |
|
747 case VG_BLEND_SRC_OVER: |
|
748 { |
|
749 r.r = rMul1(s.r, d.r, 255 - im.r); |
|
750 r.g = rMul1(s.g, d.g, 255 - im.g); |
|
751 r.b = rMul1(s.b, d.b, 255 - im.b); |
|
752 r.a = rMul1(s.a, d.a, 255 - s.a); |
|
753 break; |
|
754 } |
|
755 case VG_BLEND_DST_OVER: |
|
756 { |
|
757 r = blendIntegerColors(s, d, blendMode); |
|
758 break; |
|
759 } |
|
760 case VG_BLEND_SRC_IN: |
|
761 { |
|
762 r = blendIntegerColors(s, d, blendMode); |
|
763 break; |
|
764 } |
|
765 case VG_BLEND_DST_IN: |
|
766 { |
|
767 r.r = cMul(d.r, im.r); |
|
768 r.g = cMul(d.g, im.g); |
|
769 r.b = cMul(d.b, im.b); |
|
770 r.a = cMul(d.a, s.a); |
|
771 break; |
|
772 } |
|
773 case VG_BLEND_MULTIPLY: |
|
774 { |
|
775 RIuint32 iadst; |
|
776 iadst = 255 - d.a; |
|
777 r.r = rMul2(s.r, d.r, iadst + d.r, 255 - im.r); |
|
778 r.g = rMul2(s.g, d.g, iadst + d.g, 255 - im.g); |
|
779 r.b = rMul2(s.b, d.b, iadst + d.b, 255 - im.b); |
|
780 r.a = rMul1(s.a, d.a, 255 - s.a); |
|
781 break; |
|
782 } |
|
783 case VG_BLEND_SCREEN: |
|
784 { |
|
785 r = blendIntegerColors(s, d, blendMode); |
|
786 break; |
|
787 } |
|
788 case VG_BLEND_DARKEN: |
|
789 { |
|
790 RIuint32 iadst = 255 - d.a; |
|
791 r.r = cMin(rMul1(s.r, d.r, 255 - im.r), rMul1(d.r, s.r, iadst)); |
|
792 r.g = cMin(rMul1(s.g, d.g, 255 - im.g), rMul1(d.g, s.g, iadst)); |
|
793 r.b = cMin(rMul1(s.b, d.b, 255 - im.b), rMul1(d.b, s.b, iadst)); |
|
794 r.a = rMul1(s.a, d.a, 255 - s.a); |
|
795 break; |
|
796 } |
|
797 case VG_BLEND_LIGHTEN: |
|
798 { |
|
799 // \todo Compact darken w/r lighten? |
|
800 RIuint32 iadst = 255 - d.a; |
|
801 r.r = cMax(rMul1(s.r, d.r, 255 - im.r), rMul1(d.r, s.r, iadst)); |
|
802 r.g = cMax(rMul1(s.g, d.g, 255 - im.g), rMul1(d.g, s.g, iadst)); |
|
803 r.b = cMax(rMul1(s.b, d.b, 255 - im.b), rMul1(d.b, s.b, iadst)); |
|
804 //although the statement below is equivalent to r.a = s.a + d.a * (1.0f - s.a) |
|
805 //in practice there can be a very slight difference because |
|
806 //of the max operation in the blending formula that may cause color to exceed alpha. |
|
807 //Because of this, we compute the result both ways and return the maximum. |
|
808 r.a = cMax(rMul1(s.a, d.a, 255 - s.a), rMul1(d.a, s.a, iadst)); |
|
809 break; |
|
810 } |
|
811 default: |
|
812 { |
|
813 RI_ASSERT(blendMode == VG_BLEND_ADDITIVE); |
|
814 return blendIntegerColors(s, d, blendMode); |
|
815 break; |
|
816 } |
|
817 } |
|
818 return r; |
|
819 |
|
820 } |
|
821 |
|
822 /** |
|
823 * \brief Perform SRC_OVER and apply coverage in a single operation. |
|
824 * \note It is possible to do optimizations like this for other blending operations, |
|
825 * but they are not as widely used -> optimize if there is a requirement. |
|
826 * \note Prints are included because GDB is confused about the value of r. |
|
827 */ |
|
828 static RI_INLINE IntegerColor srcOverCoverage(const IntegerColor& s, const IntegerColor& d, RIuint32 cov) |
|
829 { |
|
830 IntegerColor r; |
|
831 RIuint32 ac = ((s.a + (s.a>>7)) * cov); |
|
832 ac = (ac + (1<<7))>>8; |
|
833 RIuint32 ia = 256 - ac; |
|
834 |
|
835 r.r = rMul2(s.r, d.r, cov, ia); |
|
836 r.g = rMul2(s.g, d.g, cov, ia); |
|
837 r.b = rMul2(s.b, d.b, cov, ia); |
|
838 r.a = rMul2(s.a, d.a, cov, ia); |
|
839 //r.r = (s.r * cov + d.r * ia) >> 8; |
|
840 //r.g = (s.g * cov + d.g * ia) >> 8; |
|
841 //r.b = (s.b * cov + d.b * ia) >> 8; |
|
842 //r.a = (s.a * cov + d.a * ia) >> 8; |
|
843 |
|
844 #if defined(RI_DEBUG) |
|
845 if (!(r.r <= r.a && r.g <= r.a && r.b <= r.a && r.a <= 255)) |
|
846 { |
|
847 printf("r: %d, g: %d, b: %d, a: %d\n",r.r,r.g,r.b,r.a); |
|
848 RI_ASSERT(false); |
|
849 } |
|
850 //RI_ASSERT(r.r <= 255 && r.g <= 255 && r.b <= 255 && r.a <= 255); |
|
851 #endif |
|
852 |
|
853 return r; |
|
854 } |
|
855 |
|
856 /** |
|
857 * \brief Check if converting between two color formats requires a gamma-conversion. |
|
858 * \todo Move this to descriptor class. |
|
859 */ |
|
860 static RI_INLINE bool needGammaConvert(const Color::Descriptor& srcDesc, const Color::Descriptor& dstDesc) |
|
861 { |
|
862 //if ((!srcDesc.isAlphaOnly()) && (srcDesc.isNonlinear() != dstDesc.isNonlinear())) |
|
863 //return true; |
|
864 if ((srcDesc.isNonlinear() != dstDesc.isNonlinear())) |
|
865 return true; |
|
866 |
|
867 return false; |
|
868 } |
|
869 |
|
870 |
|
871 RI_INLINE static bool preBlendPremultiplication(const PixelPipe::SignatureState& state) |
|
872 { |
|
873 // \todo Simplify the rules (see the corresponding places in the pixelpipe |
|
874 const bool colorTransform = state.hasColorTransform; |
|
875 |
|
876 if (PixelPipe::isImageOnly(state)) |
|
877 { |
|
878 if (colorTransform) |
|
879 return true; |
|
880 |
|
881 // Gamma conversion will leave the result premultiplied |
|
882 if (needGammaConvert(state.imageDesc, state.dstDesc)) |
|
883 return true; |
|
884 //if (state.imageDesc.isAlphaOnly()) |
|
885 //return false; |
|
886 |
|
887 return !imagePremultipliedAfterSampling(state); |
|
888 } |
|
889 |
|
890 if (state.hasImage) |
|
891 { |
|
892 if (state.imageMode == VG_DRAW_IMAGE_NORMAL) |
|
893 return !imagePremultipliedAfterSampling(state); |
|
894 // Image color has been combined with the paint color and that requires premultiplication |
|
895 if (state.imageMode == VG_DRAW_IMAGE_MULTIPLY) |
|
896 return false; // Always results in a premultiplied output color |
|
897 |
|
898 return false; // ? |
|
899 } |
|
900 |
|
901 if (state.paintType == VG_PAINT_TYPE_COLOR) |
|
902 return false; |
|
903 |
|
904 if (state.paintType != VG_PAINT_TYPE_PATTERN) |
|
905 return !gradientPremultipliedAfterSampling(state); |
|
906 |
|
907 // Must be pattern |
|
908 RI_ASSERT(state.paintType == VG_PAINT_TYPE_PATTERN); |
|
909 |
|
910 if (state.hasColorTransform) |
|
911 return true; |
|
912 |
|
913 if (needGammaConvert(state.patternDesc, state.dstDesc)) |
|
914 return true; |
|
915 |
|
916 return !patternPremultipliedAfterSampling(state); |
|
917 } |
|
918 |
|
919 /** |
|
920 * \brief Apply coverage [0 .. 256] on color |
|
921 * \note This is actually "just coverage". |
|
922 */ |
|
923 RI_INLINE static IntegerColor srcCoverage(const IntegerColor& s, const IntegerColor& d, RIuint32 cov) |
|
924 { |
|
925 IntegerColor r; |
|
926 RIuint32 icov = 256-cov; |
|
927 // Make function for multiplication between fixed point values (coverage is |
|
928 // a proper [0 .. 1] value. |
|
929 r.r = (s.r * cov + d.r * icov) >> 8; |
|
930 r.g = (s.g * cov + d.g * icov) >> 8; |
|
931 r.b = (s.b * cov + d.b * icov) >> 8; |
|
932 r.a = (s.a * cov + d.a * icov) >> 8; |
|
933 |
|
934 RI_ASSERT(r.r <= 255 && r.g <= 255 && r.b <= 255 && r.a <= 255); |
|
935 |
|
936 return r; |
|
937 } |
|
938 |
|
939 /** |
|
940 * \brief Converts color gamma only. Care must be taken concerning luminance color formats. |
|
941 * \return Converted color in "color". This will always be unpremultiplied if gamma conversion |
|
942 * takes place, i.e, tries to minimize the amount of further conversions. |
|
943 */ |
|
944 RI_INLINE static void maybeGammaConvert(const Color::Descriptor& srcDesc, const Color::Descriptor& dstDesc, IntegerColor& color, bool inputPremultiplied) |
|
945 { |
|
946 if (needGammaConvert(srcDesc, dstDesc)) |
|
947 { |
|
948 if (inputPremultiplied) |
|
949 color.unpremultiply(); |
|
950 //color.unpremultiply(srcDesc.isLuminance()); |
|
951 |
|
952 if (dstDesc.isNonlinear()) |
|
953 color.linearToGamma(); |
|
954 else |
|
955 color.gammaToLinear(); |
|
956 } |
|
957 // Output always unpremultiplied if gamma conversion takes place |
|
958 } |
|
959 |
|
960 /** |
|
961 * \brief Integer pixel-pipeline. |
|
962 * \note See internal_formats.txt for info on how the data is passed within the pipeline |
|
963 */ |
|
964 RI_INLINE static void intPixelPipe(const PixelPipe::SignatureState& signatureState, const PixelPipe::PPUniforms &uniforms, PixelPipe::PPVariants& variants) |
|
965 { |
|
966 const RIuint32 ppMaxCoverage = Rasterizer::MAX_COVERAGE << (8 - Rasterizer::SAMPLE_BITS); |
|
967 RIuint32 coverage = variants.coverage << (8 - Rasterizer::SAMPLE_BITS); |
|
968 IntegerColor out; |
|
969 IntegerColor imageColor; // imagemode != normal |
|
970 const Color::Descriptor& dstDesc = signatureState.dstDesc; |
|
971 const Color::Descriptor& patternDesc = signatureState.patternDesc; |
|
972 const Color::Descriptor& imageDesc = signatureState.imageDesc; |
|
973 |
|
974 if (!PixelPipe::isImageOnly(signatureState)) |
|
975 { |
|
976 switch(signatureState.paintType) |
|
977 { |
|
978 case VG_PAINT_TYPE_COLOR: |
|
979 out = uniforms.solidColor; |
|
980 break; |
|
981 case VG_PAINT_TYPE_LINEAR_GRADIENT: |
|
982 out = intLinearGradient(signatureState, uniforms, variants); |
|
983 variants.sx += uniforms.dgdx; |
|
984 // \todo Optimize this so that the lookup is in premultiplied dst format! |
|
985 // How about image-operations? |
|
986 if ((signatureState.imageMode != VG_DRAW_IMAGE_MULTIPLY) && dstDesc.isLuminance()) |
|
987 { |
|
988 out.fullRGBToLuminance(true, dstDesc.isNonlinear(), true, dstDesc.isNonlinear()); |
|
989 } |
|
990 break; |
|
991 case VG_PAINT_TYPE_RADIAL_GRADIENT: |
|
992 out = intRadialGradient(signatureState, uniforms, variants); |
|
993 variants.rx += uniforms.rdxdx; |
|
994 variants.ry += uniforms.rdydx; |
|
995 |
|
996 // \todo Optimize this so that the lookup is in premultiplied dst format! |
|
997 if ((signatureState.imageMode != VG_DRAW_IMAGE_MULTIPLY) && dstDesc.isLuminance()) |
|
998 { |
|
999 out.fullRGBToLuminance(true, dstDesc.isNonlinear(), true, dstDesc.isNonlinear()); |
|
1000 } |
|
1001 break; |
|
1002 default: |
|
1003 RI_ASSERT(signatureState.paintType == VG_PAINT_TYPE_PATTERN); |
|
1004 out = intPattern(signatureState, uniforms, variants); |
|
1005 // color-space == pattern color-space, not always premultiplied, expanded |
|
1006 // |
|
1007 // \todo Only increment the proper pixel-counters. This requires detecting the |
|
1008 // transform type before generating the pixel-pipeline. |
|
1009 // \note Implement fastpaths for at least identity transform with image edges coinciding |
|
1010 // with the pixel edges. <- This has been done for images. |
|
1011 variants.sx += uniforms.paint_dxdx; |
|
1012 variants.sy += uniforms.paint_dydx; |
|
1013 |
|
1014 if (!patternDesc.hasAlpha()) |
|
1015 out.a = 255; |
|
1016 |
|
1017 if (!signatureState.hasImage) |
|
1018 { |
|
1019 out = maybeColorTransform(signatureState, out, uniforms.colorTransformValues, patternDesc.isNonlinear()); |
|
1020 const bool tmpPre = patternPremultipliedAfterSampling(signatureState) && !signatureState.hasColorTransform; |
|
1021 const bool outLuminance = !signatureState.hasColorTransform && imageDesc.isLuminance(); |
|
1022 |
|
1023 if (outLuminance != dstDesc.isLuminance()) |
|
1024 { |
|
1025 if (outLuminance) |
|
1026 out.fullLuminanceToRGB(tmpPre, patternDesc.isNonlinear(), tmpPre, patternDesc.isNonlinear()); |
|
1027 else |
|
1028 out.fullRGBToLuminance(tmpPre, patternDesc.isNonlinear(), tmpPre, patternDesc.isNonlinear()); |
|
1029 } |
|
1030 maybeGammaConvert(patternDesc, dstDesc, out, tmpPre); |
|
1031 } |
|
1032 |
|
1033 break; |
|
1034 } |
|
1035 } |
|
1036 |
|
1037 if (signatureState.hasImage) |
|
1038 { |
|
1039 switch (signatureState.imageGradientType) |
|
1040 { |
|
1041 case PixelPipe::GRADIENT_TYPE_INTEGER: |
|
1042 { |
|
1043 void* addr = Image::calculateAddress(uniforms.imagePtr, imageDesc.bitsPerPixel, variants.iImageX, variants.iImageY, uniforms.imageStride); |
|
1044 RIuint32 packedImageColor = Image::readPackedPixelFromAddress(addr, imageDesc.bitsPerPixel, variants.iImageX); |
|
1045 imageColor.fromPackedColor(packedImageColor, imageDesc); |
|
1046 imageColor.expandColor(imageDesc); |
|
1047 // color-space == image color-space, not always premultiplied, expanded |
|
1048 |
|
1049 // Only integer image-gradient can have unsafe image data as an input at the moment. |
|
1050 if (signatureState.unsafeImageInput) |
|
1051 { |
|
1052 if (imageDesc.hasAlpha() && imageDesc.isPremultiplied()) |
|
1053 imageColor.clampToAlpha(); |
|
1054 } |
|
1055 |
|
1056 variants.iImageX += uniforms.image_idxdx; |
|
1057 variants.iImageY += uniforms.image_idydx; |
|
1058 break; |
|
1059 } |
|
1060 case PixelPipe::GRADIENT_TYPE_FIXED: |
|
1061 { |
|
1062 RI_ASSERT(!signatureState.unsafeImageInput); |
|
1063 |
|
1064 RIint32 sx, sy; |
|
1065 sx = variants.iImageX; |
|
1066 sy = variants.iImageY; |
|
1067 applyPatternRepeat(sx, sy, PixelPipe::TILING_MODE_PAD); |
|
1068 sx = gradientToFixedCoords(sx, uniforms.image_iWidth); |
|
1069 sy = gradientToFixedCoords(sy, uniforms.image_iHeight); |
|
1070 imageColor = intSampleImage( |
|
1071 uniforms.imagePtr, |
|
1072 uniforms.imageStride, |
|
1073 uniforms.image_iWidth, |
|
1074 uniforms.image_iHeight, |
|
1075 imageDesc, |
|
1076 sx, sy, signatureState.imageSampler, PixelPipe::TILING_MODE_PAD, NULL); |
|
1077 |
|
1078 variants.iImageX += uniforms.image_idxdx; |
|
1079 variants.iImageY += uniforms.image_idydx; |
|
1080 break; |
|
1081 } |
|
1082 default: |
|
1083 { |
|
1084 RI_ASSERT(signatureState.imageGradientType == PixelPipe::GRADIENT_TYPE_FLOAT); |
|
1085 RI_ASSERT(!signatureState.unsafeImageInput); |
|
1086 |
|
1087 RIfloat fx, fy, fw, rw; |
|
1088 fx = variants.fImageX; |
|
1089 fy = variants.fImageY; |
|
1090 fw = variants.fImageW; |
|
1091 rw = 1.0f / fw; |
|
1092 RIint32 sx0, sy0; |
|
1093 fx = RI_CLAMP(fx * rw, 0.0f, uniforms.image_fWidth - 1.0f); // \todo fImageMaxX |
|
1094 fy = RI_CLAMP(fy * rw, 0.0f, uniforms.image_fHeight - 1.0f); |
|
1095 sx0 = RI_ROUND_TO_INT(fx * (1<<PixelPipe::SAMPLE_BITS)); |
|
1096 sy0 = RI_ROUND_TO_INT(fy * (1<<PixelPipe::SAMPLE_BITS)); |
|
1097 |
|
1098 imageColor = intSampleImage( |
|
1099 uniforms.imagePtr, |
|
1100 uniforms.imageStride, |
|
1101 uniforms.image_iWidth, |
|
1102 uniforms.image_iHeight, |
|
1103 imageDesc, |
|
1104 sx0, sy0, signatureState.imageSampler, PixelPipe::TILING_MODE_PAD, NULL); |
|
1105 |
|
1106 variants.fImageX += uniforms.image_fdxdx; |
|
1107 variants.fImageY += uniforms.image_fdydx; |
|
1108 variants.fImageW += uniforms.image_fdwdx; |
|
1109 break; |
|
1110 } |
|
1111 } |
|
1112 |
|
1113 if (!imageDesc.hasAlpha()) |
|
1114 imageColor.a = 255; |
|
1115 |
|
1116 if (PixelPipe::isImageOnly(signatureState)) |
|
1117 { |
|
1118 RI_ASSERT(signatureState.imageMode == VG_DRAW_IMAGE_NORMAL); |
|
1119 out = maybeColorTransform(signatureState, imageColor, uniforms.colorTransformValues, imageDesc.isNonlinear()); |
|
1120 |
|
1121 const bool tmpPre = imagePremultipliedAfterSampling(signatureState) && !signatureState.hasColorTransform; |
|
1122 const bool outLuminance = !signatureState.hasColorTransform && imageDesc.isLuminance(); |
|
1123 |
|
1124 // Color-format conversion to dst before blending. |
|
1125 if (outLuminance != dstDesc.isLuminance()) |
|
1126 { |
|
1127 if (outLuminance) |
|
1128 out.fullLuminanceToRGB(tmpPre, imageDesc.isNonlinear(), tmpPre, imageDesc.isNonlinear()); |
|
1129 else |
|
1130 out.fullRGBToLuminance(tmpPre, imageDesc.isNonlinear(), tmpPre, imageDesc.isNonlinear()); |
|
1131 } |
|
1132 maybeGammaConvert(imageDesc, dstDesc, out, tmpPre); |
|
1133 |
|
1134 //if (!signatureState.hasColorTransform) |
|
1135 //out.premultiply(); |
|
1136 } |
|
1137 else |
|
1138 { |
|
1139 RI_ASSERT(signatureState.imageMode != VG_DRAW_IMAGE_NORMAL); |
|
1140 |
|
1141 if (!imagePremultipliedAfterSampling(signatureState)) |
|
1142 imageColor.premultiply(); |
|
1143 |
|
1144 if (signatureState.imageMode == VG_DRAW_IMAGE_MULTIPLY) |
|
1145 { |
|
1146 if (signatureState.paintType == VG_PAINT_TYPE_PATTERN && |
|
1147 !patternPremultipliedAfterSampling(signatureState)) |
|
1148 { |
|
1149 out.premultiply(); |
|
1150 } |
|
1151 |
|
1152 out.r = cMul(out.r, imageColor.r); |
|
1153 out.g = cMul(out.g, imageColor.g); |
|
1154 out.b = cMul(out.b, imageColor.b); |
|
1155 out.a = cMul(out.a, imageColor.a); |
|
1156 |
|
1157 out = maybeColorTransform(signatureState, out, uniforms.colorTransformValues, imageDesc.isNonlinear()); |
|
1158 //const bool outLuminance = !signatureState.hasColorTransform && imageDesc.isLuminance(); |
|
1159 // Color transform will always result in RGB, regardless of input. |
|
1160 const bool outLuminance = (imageDesc.isLuminance() && !paintInRGB(signatureState)) && !signatureState.hasColorTransform; |
|
1161 if (!outLuminance && dstDesc.isLuminance()) |
|
1162 { |
|
1163 // Convert to destination (luminance) |
|
1164 out.fullRGBToLuminance(!signatureState.hasColorTransform, imageDesc.isNonlinear(), true, dstDesc.isNonlinear()); |
|
1165 } |
|
1166 else if (imageDesc.isNonlinear() != dstDesc.isNonlinear()) |
|
1167 { |
|
1168 // Non-luminance gamma |
|
1169 if (!signatureState.hasColorTransform) |
|
1170 out.unpremultiply(); |
|
1171 |
|
1172 if (dstDesc.isNonlinear()) |
|
1173 out.linearToGamma(); |
|
1174 else |
|
1175 out.gammaToLinear(); |
|
1176 |
|
1177 out.premultiply(); |
|
1178 } |
|
1179 else if (signatureState.hasColorTransform) |
|
1180 out.premultiply(); |
|
1181 |
|
1182 // Output dst and premultiplied. |
|
1183 } |
|
1184 else |
|
1185 { |
|
1186 RI_ASSERT(signatureState.imageMode == VG_DRAW_IMAGE_STENCIL); |
|
1187 IntegerColor alphas, pr; |
|
1188 |
|
1189 if (signatureState.paintType == VG_PAINT_TYPE_PATTERN) |
|
1190 { |
|
1191 out = maybeColorTransform(signatureState, out, uniforms.colorTransformValues, patternDesc.isNonlinear()); |
|
1192 const bool isLuminance = patternDesc.isLuminance() && !signatureState.hasColorTransform; |
|
1193 // If using pattern, convert to destination color-space |
|
1194 // \todo If not, handle this when the lookups are generated. |
|
1195 if (isLuminance != dstDesc.isLuminance()) |
|
1196 { |
|
1197 out.fullRGBToLuminance(patternPremultipliedAfterSampling(signatureState) && !signatureState.hasColorTransform, patternDesc.isNonlinear(), true, dstDesc.isNonlinear()); |
|
1198 } |
|
1199 else if (patternDesc.isNonlinear() != dstDesc.isNonlinear()) |
|
1200 { |
|
1201 if (patternPremultipliedAfterSampling(signatureState) && !signatureState.hasColorTransform) |
|
1202 out.unpremultiply(); |
|
1203 |
|
1204 if (dstDesc.isNonlinear()) |
|
1205 out.linearToGamma(); |
|
1206 else |
|
1207 out.gammaToLinear(); |
|
1208 |
|
1209 out.premultiply(); |
|
1210 } else if (signatureState.hasColorTransform || !patternPremultipliedAfterSampling(signatureState)) |
|
1211 out.premultiply(); |
|
1212 } |
|
1213 |
|
1214 if (dstDesc.isLuminance() && !imageDesc.isLuminance()) |
|
1215 { |
|
1216 // Convert image to luminance |
|
1217 imageColor.rgbToLuminance(); |
|
1218 imageColor.r = imageColor.b = imageColor.b = RI_INT_MIN(imageColor.r, imageColor.a); |
|
1219 } |
|
1220 |
|
1221 #if defined(RI_DEBUG) && 0 |
|
1222 printf("stencil r: %d, g: %d, b: %d, a: %d\n",imageColor.r,imageColor.g,imageColor.b,imageColor.a); |
|
1223 printf("input r: %d, g: %d, b: %d, a: %d\n",out.r,out.g,out.b,out.a); |
|
1224 #endif |
|
1225 if (signatureState.paintType == VG_PAINT_TYPE_COLOR) |
|
1226 { |
|
1227 // Better precision for solid color input. |
|
1228 // Compute alpha channels |
|
1229 alphas.r = rMul(out.a, imageColor.r); |
|
1230 alphas.g = rMul(out.a, imageColor.g); |
|
1231 alphas.b = rMul(out.a, imageColor.b); |
|
1232 // Premultiply |
|
1233 pr.r = rMul(out.r, imageColor.r); |
|
1234 pr.g = rMul(out.g, imageColor.g); |
|
1235 pr.b = rMul(out.b, imageColor.b); |
|
1236 pr.a = rMul(out.a, imageColor.a); |
|
1237 } |
|
1238 else |
|
1239 { |
|
1240 // Compute alpha channels |
|
1241 alphas.r = cMul(out.a, imageColor.r); |
|
1242 alphas.g = cMul(out.a, imageColor.g); |
|
1243 alphas.b = cMul(out.a, imageColor.b); |
|
1244 // Premultiply |
|
1245 pr.r = cMul(out.r, imageColor.r); |
|
1246 pr.g = cMul(out.g, imageColor.g); |
|
1247 pr.b = cMul(out.b, imageColor.b); |
|
1248 pr.a = cMul(out.a, imageColor.a); |
|
1249 } |
|
1250 #if defined(RI_DEBUG) && 0 |
|
1251 printf("alphas r: %d, g: %d, b: %d, a: %d\n",alphas.r,alphas.g,alphas.b,alphas.a); |
|
1252 printf("pr r: %d, g: %d, b: %d, a: %d\n",pr.r,pr.g,pr.b,pr.a); |
|
1253 #endif |
|
1254 out = pr; |
|
1255 imageColor = alphas; |
|
1256 } |
|
1257 } |
|
1258 } |
|
1259 |
|
1260 if (signatureState.hasMasking) |
|
1261 { |
|
1262 // \todo Read and process only the proper component of the mask pixel. |
|
1263 const int maskBpp = signatureState.maskDesc.bitsPerPixel; |
|
1264 |
|
1265 RIuint32 packedMaskColor = Image::readPackedPixelFromAddress(variants.maskPtr, maskBpp, variants.dstX); |
|
1266 IntegerColor maskColor; |
|
1267 maskColor.fromPackedMask(packedMaskColor, signatureState.maskDesc); |
|
1268 maskColor.expandMask(signatureState.maskDesc); |
|
1269 |
|
1270 RIuint32 maskCoverage = maskColor.a + (maskColor.a >> 7); |
|
1271 coverage = (coverage * maskCoverage) >> 8; |
|
1272 |
|
1273 variants.maskPtr = (void*)Image::incrementPointer(variants.maskPtr, maskBpp, variants.dstX); |
|
1274 } |
|
1275 |
|
1276 #if defined(RI_DEBUG) |
|
1277 IntegerColor preblend = out; |
|
1278 #endif |
|
1279 // \todo Coverage check for pixelpipes != solid color with solid output colors? |
|
1280 |
|
1281 IntegerColor d(0,0,0,0); |
|
1282 |
|
1283 // All operations that depend on DST are done next. Keep it organized like that. |
|
1284 if ((coverage < ppMaxCoverage) || (out.a < 255) || alwaysLoadDst(signatureState)) |
|
1285 { |
|
1286 d = IntegerColor(Image::readPackedPixelFromAddress( |
|
1287 variants.dst, dstDesc.bitsPerPixel, variants.dstX), dstDesc); |
|
1288 d.expandColor(dstDesc); |
|
1289 |
|
1290 if (!dstDesc.isPremultiplied()) |
|
1291 { |
|
1292 d.premultiply(); |
|
1293 } |
|
1294 |
|
1295 // Premultiply output |
|
1296 #if 0 |
|
1297 if (!PixelPipe::isImageOnly(signatureState)) |
|
1298 { |
|
1299 if (signatureState.paintType == VG_PAINT_TYPE_PATTERN && !patternPremultipliedAfterSampling(signatureState)) |
|
1300 out.premultiply(); |
|
1301 else if (signatureState.hasImage && !imagePremultipliedAfterSampling(signatureState)) |
|
1302 out.premultiply(); |
|
1303 } |
|
1304 #endif |
|
1305 |
|
1306 if (!signatureState.isRenderToMask) |
|
1307 { |
|
1308 VGBlendMode bm = signatureState.blendMode; |
|
1309 |
|
1310 // Currently SRC requires premultiplication even when only applying coverage. |
|
1311 //if (bm != VG_BLEND_SRC) |
|
1312 { |
|
1313 // If the src color has not been premultiplied before, now's the time. |
|
1314 // \todo Fast path for src alpha == 255 and SRC_OVER? Others? |
|
1315 if (preBlendPremultiplication(signatureState)) |
|
1316 out.premultiply(); |
|
1317 } |
|
1318 |
|
1319 if (signatureState.hasImage && signatureState.imageMode == VG_DRAW_IMAGE_STENCIL) |
|
1320 { |
|
1321 out = blendIntegerStencil(out, imageColor, d, bm); |
|
1322 } |
|
1323 else |
|
1324 { |
|
1325 switch(bm) |
|
1326 { |
|
1327 case VG_BLEND_SRC_OVER: |
|
1328 out = srcOverCoverage(out, d, coverage); |
|
1329 break; |
|
1330 case VG_BLEND_SRC: |
|
1331 out = srcCoverage(out, d, coverage); |
|
1332 break; |
|
1333 default: |
|
1334 out = blendIntegerColors(out, d, bm); |
|
1335 out = srcCoverage(out, d, coverage); |
|
1336 break; |
|
1337 } |
|
1338 } |
|
1339 |
|
1340 #if defined(RI_DEBUG) |
|
1341 if (dstDesc.isPremultiplied()) |
|
1342 { |
|
1343 RI_ASSERT(out.r <= out.a); |
|
1344 RI_ASSERT(out.g <= out.a); |
|
1345 RI_ASSERT(out.b <= out.a); |
|
1346 } |
|
1347 #endif |
|
1348 |
|
1349 } |
|
1350 else |
|
1351 { |
|
1352 // Mask operation |
|
1353 out = intMaskOperation(coverage, d, signatureState.maskOperation); |
|
1354 } |
|
1355 |
|
1356 // out is always premultiplied at this point. Must be in destination color-space |
|
1357 if (!dstDesc.isPremultiplied()) |
|
1358 { |
|
1359 // Unpremultiply if output is not premultiplied |
|
1360 out.unpremultiply(); |
|
1361 } |
|
1362 } |
|
1363 else |
|
1364 { |
|
1365 // Unpremultiply, ... |
|
1366 if (!dstDesc.isPremultiplied()) |
|
1367 out.unpremultiply(); |
|
1368 } |
|
1369 |
|
1370 // VG_SET_MASK does not require dst load: |
|
1371 if (signatureState.isRenderToMask && signatureState.maskOperation == VG_SET_MASK) |
|
1372 out = intMaskOperation(coverage, d, VG_SET_MASK); |
|
1373 |
|
1374 out.truncateColor(dstDesc); |
|
1375 Image::writePackedPixelToAddress( |
|
1376 variants.dst, dstDesc.bitsPerPixel, variants.dstX, out.getPackedColor(dstDesc)); |
|
1377 |
|
1378 // \todo X for bpp < 8 |
|
1379 variants.dst = (void*)Image::incrementPointer(variants.dst, dstDesc.bitsPerPixel, variants.dstX); |
|
1380 //variants.dst = colorBuffer->advancePointer(variants.dst); |
|
1381 variants.dstX++; |
|
1382 } |
|
1383 |
|
1384 RI_INLINE static void fillSolidSpan(const PixelPipe::SignatureState& state, const PixelPipe::PPUniforms& uniforms, int startX, int y, int nPixels, RIuint32 packedColor) |
|
1385 { |
|
1386 Image::fillPackedPixels((void*)uniforms.dstPtr, state.dstDesc.bitsPerPixel, startX, y, uniforms.dstStride, nPixels, packedColor); |
|
1387 } |
|
1388 |
|
1389 /** |
|
1390 * \brief This will calculate all the pixel-pipeline variants that need to be updated per-pixel. |
|
1391 * \note There may be a need for a different, faster function for image rendering, where |
|
1392 * there are faster methods of updating the variants. |
|
1393 */ |
|
1394 RI_INLINE static void prepareSpanVariants(const PixelPipe::SignatureState& state, const PixelPipe::PPUniforms& uniforms, const Span& span, PixelPipe::PPVariants& variants) |
|
1395 { |
|
1396 //variants.dst = uniforms.dst->calculateAddress(span.x0, span.y); |
|
1397 variants.dst = Image::calculateAddress(uniforms.dstPtr, state.dstDesc.bitsPerPixel, span.x0, span.y, uniforms.dstStride); |
|
1398 variants.dstX = span.x0; |
|
1399 variants.coverage = span.coverage; |
|
1400 |
|
1401 if (state.paintType != VG_PAINT_TYPE_COLOR) |
|
1402 { |
|
1403 if (state.paintType == VG_PAINT_TYPE_LINEAR_GRADIENT) |
|
1404 { |
|
1405 // \todo Adjust pixel-center. |
|
1406 int x = uniforms.dgdx * span.x0 + uniforms.dgdy * span.y + uniforms.lgc; |
|
1407 variants.sx = x; |
|
1408 } |
|
1409 else if (state.paintType == VG_PAINT_TYPE_RADIAL_GRADIENT) |
|
1410 { |
|
1411 RGScalar x = uniforms.rdxdx * (RGScalar)span.x0 + uniforms.rdxdy * (RGScalar)span.y; |
|
1412 RGScalar y = uniforms.rdydy * (RGScalar)span.y + uniforms.rdydx * (RGScalar)span.x0; |
|
1413 |
|
1414 variants.rx = x + uniforms.rx0; |
|
1415 variants.ry = y + uniforms.ry0; |
|
1416 } |
|
1417 else |
|
1418 { |
|
1419 RI_ASSERT(state.paintType == VG_PAINT_TYPE_PATTERN); |
|
1420 variants.sx = uniforms.paint_dxdx * span.x0 + uniforms.paint_dxdy * span.y + uniforms.paint_x0; |
|
1421 variants.sy = uniforms.paint_dydy * span.y + uniforms.paint_dydx * span.x0 + uniforms.paint_y0; |
|
1422 } |
|
1423 } |
|
1424 |
|
1425 if (state.hasMasking) |
|
1426 { |
|
1427 variants.maskPtr = Image::calculateAddress(uniforms.maskPtr, state.maskDesc.bitsPerPixel, span.x0, span.y, uniforms.maskStride); |
|
1428 } |
|
1429 |
|
1430 if (state.hasImage) |
|
1431 { |
|
1432 switch (state.imageGradientType) |
|
1433 { |
|
1434 case PixelPipe::GRADIENT_TYPE_INTEGER: |
|
1435 case PixelPipe::GRADIENT_TYPE_FIXED: |
|
1436 variants.iImageX = uniforms.image_ix0 + span.x0 * uniforms.image_idxdx + span.y * uniforms.image_idxdy; |
|
1437 variants.iImageY = uniforms.image_iy0 + span.y * uniforms.image_idydy + span.x0 * uniforms.image_idydx; |
|
1438 break; |
|
1439 default: |
|
1440 RI_ASSERT(state.imageGradientType == PixelPipe::GRADIENT_TYPE_FLOAT); |
|
1441 variants.fImageX = uniforms.image_fx0 + span.x0 * uniforms.image_fdxdx + span.y * uniforms.image_fdxdy; |
|
1442 variants.fImageY = uniforms.image_fy0 + span.y * uniforms.image_fdydy + span.x0 * uniforms.image_fdydx; |
|
1443 variants.fImageW = uniforms.image_fw0 + span.x0 * uniforms.image_fdwdx + span.y * uniforms.image_fdwdy; |
|
1444 break; |
|
1445 } |
|
1446 } |
|
1447 } |
|
1448 |
|
1449 void executePixelPipeline(const PixelPipe::SignatureState& state, const PixelPipe::PPUniforms& uniforms, PixelPipe::PPVariants& variants, const Span* spans, int nSpans) |
|
1450 { |
|
1451 RI_ASSERT(nSpans > 0); |
|
1452 for (int i = 0; i < nSpans; i++) |
|
1453 { |
|
1454 const Span& s = spans[i]; |
|
1455 |
|
1456 if (s.coverage != Rasterizer::MAX_COVERAGE || !canSolidFill(state)) |
|
1457 { |
|
1458 int n = s.len; |
|
1459 RI_ASSERT(n); |
|
1460 prepareSpanVariants(state, uniforms, s, variants); |
|
1461 |
|
1462 do { |
|
1463 intPixelPipe(state, uniforms, variants); |
|
1464 } while (--n); |
|
1465 } else |
|
1466 { |
|
1467 fillSolidSpan(state, uniforms, s.x0, s.y, s.len, uniforms.packedSolidColor); |
|
1468 } |
|
1469 } |
|
1470 |
|
1471 } |
|
1472 |
|
1473 void calculatePPHash(PixelPipeHash& hash, const PixelPipe::SignatureState& derivedState) |
|
1474 { |
|
1475 const RIuint32 blendModeBits = 4; |
|
1476 const RIuint32 imageModeBits = 2; |
|
1477 const RIuint32 paintTypeBits = 2; |
|
1478 const RIuint32 tilingModeBits = 2; |
|
1479 const RIuint32 samplerBits = 1; |
|
1480 const RIuint32 imageGradientTypeBits = 2; |
|
1481 const RIuint32 boolBits = 1; |
|
1482 const RIuint32 descBits = 10; |
|
1483 const RIuint32 maskOperationBits = 3; |
|
1484 |
|
1485 RIuint32 blendMode = ((RIuint32)derivedState.blendMode) - ((RIuint32)VG_BLEND_SRC); |
|
1486 RIuint32 imageMode = ((RIuint32)derivedState.imageMode) - ((RIuint32)VG_DRAW_IMAGE_NORMAL); |
|
1487 RIuint32 paintType = ((RIuint32)derivedState.paintType) - ((RIuint32)VG_PAINT_TYPE_COLOR); |
|
1488 RIuint32 maskOperation = ((RIuint32)derivedState.maskOperation) - ((RIuint32)VG_CLEAR_MASK); |
|
1489 RIuint32 paintTilingMode = ((RIuint32)derivedState.paintTilingMode); |
|
1490 RIuint32 paintSampler = ((RIuint32)derivedState.paintSampler); |
|
1491 RIuint32 imageSampler = ((RIuint32)derivedState.imageSampler); |
|
1492 |
|
1493 RIuint32 imageGradientType = ((RIuint32)derivedState.imageGradientType); |
|
1494 |
|
1495 RIuint32 dstFormat = (RIuint32)(derivedState.dstDesc.toIndex()); |
|
1496 RIuint32 maskFormat = (RIuint32)(derivedState.maskDesc.toIndex()); |
|
1497 RIuint32 imageFormat = (RIuint32)(derivedState.imageDesc.toIndex()); |
|
1498 RIuint32 patternFormat = (RIuint32)(derivedState.patternDesc.toIndex()); |
|
1499 |
|
1500 RIuint32 hasMasking = derivedState.hasMasking ? 1 : 0; |
|
1501 RIuint32 hasImage = derivedState.hasImage ? 1 : 0; |
|
1502 RIuint32 hasColorTransform = derivedState.hasColorTransform ? 1 : 0; |
|
1503 RIuint32 isMaskOperation = derivedState.isRenderToMask ? 1 : 0; |
|
1504 RIuint32 fillColorTransparent = derivedState.fillColorTransparent ? 1 : 0; |
|
1505 RIuint32 unsafeImageInput = derivedState.unsafeImageInput ? 1 : 0; |
|
1506 |
|
1507 // Modify hashes according to relevant state: |
|
1508 int b = 0; |
|
1509 b = riInsertBits32(hash.value, sizeof(hash.value), blendMode, blendModeBits, b); |
|
1510 b = riInsertBits32(hash.value, sizeof(hash.value), imageMode, imageModeBits, b); |
|
1511 b = riInsertBits32(hash.value, sizeof(hash.value), paintType, paintTypeBits, b); |
|
1512 b = riInsertBits32(hash.value, sizeof(hash.value), maskOperation, maskOperationBits, b); |
|
1513 b = riInsertBits32(hash.value, sizeof(hash.value), paintTilingMode, tilingModeBits, b); |
|
1514 b = riInsertBits32(hash.value, sizeof(hash.value), paintSampler, samplerBits, b); |
|
1515 b = riInsertBits32(hash.value, sizeof(hash.value), imageSampler, samplerBits, b); |
|
1516 |
|
1517 b = riInsertBits32(hash.value, sizeof(hash.value), imageGradientType, imageGradientTypeBits, b); |
|
1518 |
|
1519 b = riInsertBits32(hash.value, sizeof(hash.value), dstFormat, descBits, b); |
|
1520 b = riInsertBits32(hash.value, sizeof(hash.value), maskFormat, descBits, b); |
|
1521 b = riInsertBits32(hash.value, sizeof(hash.value), imageFormat, descBits, b); |
|
1522 b = riInsertBits32(hash.value, sizeof(hash.value), patternFormat, descBits, b); |
|
1523 |
|
1524 b = riInsertBits32(hash.value, sizeof(hash.value), hasMasking, boolBits, b); |
|
1525 b = riInsertBits32(hash.value, sizeof(hash.value), hasImage, boolBits, b); |
|
1526 b = riInsertBits32(hash.value, sizeof(hash.value), hasColorTransform, boolBits, b); |
|
1527 b = riInsertBits32(hash.value, sizeof(hash.value), isMaskOperation, boolBits, b); |
|
1528 b = riInsertBits32(hash.value, sizeof(hash.value), fillColorTransparent, boolBits, b); |
|
1529 b = riInsertBits32(hash.value, sizeof(hash.value), unsafeImageInput, boolBits, b); |
|
1530 } |
|
1531 |
|
1532 } |
|
1533 |