1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <mutex>
18 #include <array>
19 #include <sstream>
20 #include <algorithm>
21
22 #include <gui/Surface.h>
23 #include <gui/BufferItemConsumer.h>
24
25 #include <ui/GraphicBuffer.h>
26 #include <math/vec4.h>
27
28 #include <GLES3/gl3.h>
29
30 #include "Hwc2TestBuffer.h"
31 #include "Hwc2TestLayers.h"
32
33 using namespace android;
34
35 /* Returns a fence from egl */
36 typedef void (*FenceCallback)(int32_t fence, void* callbackArgs);
37
38 /* Returns fence to fence generator */
39 static void setFence(int32_t fence, void* fenceGenerator);
40
41
42 /* Used to receive the surfaces and fences from egl. The egl buffers are thrown
43 * away. The fences are sent to the requester via a callback */
44 class Hwc2TestSurfaceManager {
45 public:
46 /* Listens for a new frame, detaches the buffer and returns the fence
47 * through saved callback. */
48 class BufferListener : public ConsumerBase::FrameAvailableListener {
49 public:
BufferListener(sp<IGraphicBufferConsumer> consumer,FenceCallback callback,void * callbackArgs)50 BufferListener(sp<IGraphicBufferConsumer> consumer,
51 FenceCallback callback, void* callbackArgs)
52 : mConsumer(consumer),
53 mCallback(callback),
54 mCallbackArgs(callbackArgs) { }
55
onFrameAvailable(const BufferItem &)56 void onFrameAvailable(const BufferItem& /*item*/)
57 {
58 BufferItem item;
59
60 if (mConsumer->acquireBuffer(&item, 0))
61 return;
62 if (mConsumer->detachBuffer(item.mSlot))
63 return;
64
65 mCallback(item.mFence->dup(), mCallbackArgs);
66 }
67
68 private:
69 sp<IGraphicBufferConsumer> mConsumer;
70 FenceCallback mCallback;
71 void* mCallbackArgs;
72 };
73
74 /* Creates a buffer listener that waits on a new frame from the buffer
75 * queue. */
initialize(const Area & bufferArea,android_pixel_format_t format,FenceCallback callback,void * callbackArgs)76 void initialize(const Area& bufferArea, android_pixel_format_t format,
77 FenceCallback callback, void* callbackArgs)
78 {
79 sp<IGraphicBufferProducer> producer;
80 sp<IGraphicBufferConsumer> consumer;
81 BufferQueue::createBufferQueue(&producer, &consumer);
82
83 consumer->setDefaultBufferSize(bufferArea.width, bufferArea.height);
84 consumer->setDefaultBufferFormat(format);
85
86 mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
87
88 mListener = new BufferListener(consumer, callback, callbackArgs);
89 mBufferItemConsumer->setFrameAvailableListener(mListener);
90
91 mSurface = new Surface(producer, true);
92 }
93
94 /* Used by Egl manager. The surface is never displayed. */
getSurface() const95 sp<Surface> getSurface() const
96 {
97 return mSurface;
98 }
99
100 private:
101 sp<BufferItemConsumer> mBufferItemConsumer;
102 sp<BufferListener> mListener;
103 /* Used by Egl manager. The surface is never displayed */
104 sp<Surface> mSurface;
105 };
106
107
108 /* Used to generate valid fences. It is not possible to create a dummy sync
109 * fence for testing. Egl can generate buffers along with a valid fence.
110 * The buffer cannot be guaranteed to be the same format across all devices so
111 * a CPU filled buffer is used instead. The Egl fence is used along with the
112 * CPU filled buffer. */
113 class Hwc2TestEglManager {
114 public:
Hwc2TestEglManager()115 Hwc2TestEglManager()
116 : mEglDisplay(EGL_NO_DISPLAY),
117 mEglSurface(EGL_NO_SURFACE),
118 mEglContext(EGL_NO_CONTEXT) { }
119
~Hwc2TestEglManager()120 ~Hwc2TestEglManager()
121 {
122 cleanup();
123 }
124
initialize(sp<Surface> surface)125 int initialize(sp<Surface> surface)
126 {
127 mSurface = surface;
128
129 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
130 if (mEglDisplay == EGL_NO_DISPLAY) return false;
131
132 EGLint major;
133 EGLint minor;
134 if (!eglInitialize(mEglDisplay, &major, &minor)) {
135 ALOGW("Could not initialize EGL");
136 return false;
137 }
138
139 /* We're going to use a 1x1 pbuffer surface later on
140 * The configuration distance doesn't really matter for what we're
141 * trying to do */
142 EGLint configAttrs[] = {
143 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
144 EGL_RED_SIZE, 8,
145 EGL_GREEN_SIZE, 8,
146 EGL_BLUE_SIZE, 8,
147 EGL_ALPHA_SIZE, 0,
148 EGL_DEPTH_SIZE, 24,
149 EGL_STENCIL_SIZE, 0,
150 EGL_NONE
151 };
152
153 EGLConfig configs[1];
154 EGLint configCnt;
155 if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1,
156 &configCnt)) {
157 ALOGW("Could not select EGL configuration");
158 eglReleaseThread();
159 eglTerminate(mEglDisplay);
160 return false;
161 }
162
163 if (configCnt <= 0) {
164 ALOGW("Could not find EGL configuration");
165 eglReleaseThread();
166 eglTerminate(mEglDisplay);
167 return false;
168 }
169
170 /* These objects are initialized below but the default "null" values are
171 * used to cleanup properly at any point in the initialization sequence */
172 EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
173 mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT,
174 attrs);
175 if (mEglContext == EGL_NO_CONTEXT) {
176 ALOGW("Could not create EGL context");
177 cleanup();
178 return false;
179 }
180
181 EGLint surfaceAttrs[] = { EGL_NONE };
182 mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0],
183 mSurface.get(), surfaceAttrs);
184 if (mEglSurface == EGL_NO_SURFACE) {
185 ALOGW("Could not create EGL surface");
186 cleanup();
187 return false;
188 }
189
190 if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
191 ALOGW("Could not change current EGL context");
192 cleanup();
193 return false;
194 }
195
196 return true;
197 }
198
makeCurrent() const199 void makeCurrent() const
200 {
201 eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
202 }
203
present() const204 void present() const
205 {
206 eglSwapBuffers(mEglDisplay, mEglSurface);
207 }
208
209 private:
cleanup()210 void cleanup()
211 {
212 if (mEglDisplay == EGL_NO_DISPLAY)
213 return;
214 if (mEglSurface != EGL_NO_SURFACE)
215 eglDestroySurface(mEglDisplay, mEglSurface);
216 if (mEglContext != EGL_NO_CONTEXT)
217 eglDestroyContext(mEglDisplay, mEglContext);
218
219 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
220 EGL_NO_CONTEXT);
221 eglReleaseThread();
222 eglTerminate(mEglDisplay);
223 }
224
225 sp<Surface> mSurface;
226 EGLDisplay mEglDisplay;
227 EGLSurface mEglSurface;
228 EGLContext mEglContext;
229 };
230
231
232 static const std::array<vec2, 4> triangles = {{
233 { 1.0f, 1.0f },
234 { -1.0f, 1.0f },
235 { 1.0f, -1.0f },
236 { -1.0f, -1.0f },
237 }};
238
239 class Hwc2TestFenceGenerator {
240 public:
241
Hwc2TestFenceGenerator()242 Hwc2TestFenceGenerator()
243 {
244 mSurfaceManager.initialize({1, 1}, HAL_PIXEL_FORMAT_RGBA_8888,
245 setFence, this);
246
247 if (!mEglManager.initialize(mSurfaceManager.getSurface()))
248 return;
249
250 mEglManager.makeCurrent();
251
252 glClearColor(0.0, 0.0, 0.0, 1.0);
253 glEnableVertexAttribArray(0);
254 }
255
~Hwc2TestFenceGenerator()256 ~Hwc2TestFenceGenerator()
257 {
258 if (mFence >= 0)
259 close(mFence);
260 mFence = -1;
261
262 mEglManager.makeCurrent();
263 }
264
265 /* It is not possible to simply generate a fence. The easiest way is to
266 * generate a buffer using egl and use the associated fence. The buffer
267 * cannot be guaranteed to be a certain format across all devices using this
268 * method. Instead the buffer is generated using the CPU */
get()269 int32_t get()
270 {
271 if (mFence >= 0) {
272 return dup(mFence);
273 }
274
275 std::unique_lock<std::mutex> lock(mMutex);
276
277 /* If the pending is still set to false and times out, we cannot recover.
278 * Set an error and return */
279 while (mPending != false) {
280 if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
281 return -ETIME;
282 }
283
284 /* Generate a fence. The fence will be returned through the setFence
285 * callback */
286 mEglManager.makeCurrent();
287
288 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, triangles.data());
289 glClear(GL_COLOR_BUFFER_BIT);
290
291 mEglManager.present();
292
293 /* Wait for the setFence callback */
294 while (mPending != true) {
295 if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
296 return -ETIME;
297 }
298
299 mPending = false;
300
301 return dup(mFence);
302 }
303
304 /* Callback that sets the fence */
set(int32_t fence)305 void set(int32_t fence)
306 {
307 mFence = fence;
308 mPending = true;
309
310 mCv.notify_all();
311 }
312
313 private:
314
315 Hwc2TestSurfaceManager mSurfaceManager;
316 Hwc2TestEglManager mEglManager;
317
318 std::mutex mMutex;
319 std::condition_variable mCv;
320
321 int32_t mFence = -1;
322 bool mPending = false;
323 };
324
325
setFence(int32_t fence,void * fenceGenerator)326 static void setFence(int32_t fence, void* fenceGenerator)
327 {
328 static_cast<Hwc2TestFenceGenerator*>(fenceGenerator)->set(fence);
329 }
330
331
332 /* Sets the pixel of a buffer given the location, format, stride and color.
333 * Currently only supports RGBA_8888 */
setColor(int32_t x,int32_t y,android_pixel_format_t format,uint32_t stride,uint8_t * img,uint8_t r,uint8_t g,uint8_t b,uint8_t a)334 static void setColor(int32_t x, int32_t y,
335 android_pixel_format_t format, uint32_t stride, uint8_t* img, uint8_t r,
336 uint8_t g, uint8_t b, uint8_t a)
337 {
338 switch (format) {
339 case HAL_PIXEL_FORMAT_RGBA_8888:
340 img[(y * stride + x) * 4 + 0] = r;
341 img[(y * stride + x) * 4 + 1] = g;
342 img[(y * stride + x) * 4 + 2] = b;
343 img[(y * stride + x) * 4 + 3] = a;
344 break;
345 default:
346 break;
347 }
348 }
349
Hwc2TestBuffer()350 Hwc2TestBuffer::Hwc2TestBuffer()
351 : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
352
353 Hwc2TestBuffer::~Hwc2TestBuffer() = default;
354
355 /* When the buffer changes sizes, save the new size and invalidate the current
356 * buffer */
updateBufferArea(const Area & bufferArea)357 void Hwc2TestBuffer::updateBufferArea(const Area& bufferArea)
358 {
359 if (mBufferArea.width == bufferArea.width
360 && mBufferArea.height == bufferArea.height)
361 return;
362
363 mBufferArea.width = bufferArea.width;
364 mBufferArea.height = bufferArea.height;
365
366 mValidBuffer = false;
367 }
368
369 /* Returns a valid buffer handle and fence. The handle is filled using the CPU
370 * to ensure the correct format across all devices. The fence is created using
371 * egl. */
get(buffer_handle_t * outHandle,int32_t * outFence)372 int Hwc2TestBuffer::get(buffer_handle_t* outHandle, int32_t* outFence)
373 {
374 if (mBufferArea.width == -1 || mBufferArea.height == -1)
375 return -EINVAL;
376
377 /* If the current buffer is valid, the previous buffer can be reused.
378 * Otherwise, create new buffer */
379 if (!mValidBuffer) {
380 int ret = generateBuffer();
381 if (ret)
382 return ret;
383 }
384
385 *outFence = mFenceGenerator->get();
386 *outHandle = mHandle;
387
388 mValidBuffer = true;
389
390 return 0;
391 }
392
393 /* CPU fills a buffer to guarantee the correct buffer format across all
394 * devices */
generateBuffer()395 int Hwc2TestBuffer::generateBuffer()
396 {
397 /* Create new graphic buffer with correct dimensions */
398 mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
399 mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
400 "hwc2_test_buffer");
401 int ret = mGraphicBuffer->initCheck();
402 if (ret) {
403 return ret;
404 }
405 if (!mGraphicBuffer->handle) {
406 return -EINVAL;
407 }
408
409 /* Locks the buffer for writing */
410 uint8_t* img;
411 mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
412
413 uint32_t stride = mGraphicBuffer->getStride();
414
415 /* Iterate from the top row of the buffer to the bottom row */
416 for (int32_t y = 0; y < mBufferArea.height; y++) {
417
418 /* Will be used as R, G and B values for pixel colors */
419 uint8_t max = 255;
420 uint8_t min = 0;
421
422 /* Divide the rows into 3 sections. The first section will contain
423 * the lighest colors. The last section will contain the darkest
424 * colors. */
425 if (y < mBufferArea.height * 1.0 / 3.0) {
426 min = 255 / 2;
427 } else if (y >= mBufferArea.height * 2.0 / 3.0) {
428 max = 255 / 2;
429 }
430
431 /* Divide the columns into 3 sections. The first section is red,
432 * the second is green and the third is blue */
433 int32_t x = 0;
434 for (; x < mBufferArea.width / 3; x++) {
435 setColor(x, y, mFormat, stride, img, max, min, min, 255);
436 }
437
438 for (; x < mBufferArea.width * 2 / 3; x++) {
439 setColor(x, y, mFormat, stride, img, min, max, min, 255);
440 }
441
442 for (; x < mBufferArea.width; x++) {
443 setColor(x, y, mFormat, stride, img, min, min, max, 255);
444 }
445 }
446
447 /* Unlock the buffer for reading */
448 mGraphicBuffer->unlock();
449
450 mHandle = mGraphicBuffer->handle;
451
452 return 0;
453 }
454
455
Hwc2TestClientTargetBuffer()456 Hwc2TestClientTargetBuffer::Hwc2TestClientTargetBuffer()
457 : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
458
~Hwc2TestClientTargetBuffer()459 Hwc2TestClientTargetBuffer::~Hwc2TestClientTargetBuffer() { }
460
461 /* Generates a client target buffer using the layers assigned for client
462 * composition. Takes into account the individual layer properties such as
463 * transform, blend mode, source crop, etc. */
get(buffer_handle_t * outHandle,int32_t * outFence,const Area & bufferArea,const Hwc2TestLayers * testLayers,const std::set<hwc2_layer_t> * clientLayers,const std::set<hwc2_layer_t> * clearLayers)464 int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle,
465 int32_t* outFence, const Area& bufferArea,
466 const Hwc2TestLayers* testLayers,
467 const std::set<hwc2_layer_t>* clientLayers,
468 const std::set<hwc2_layer_t>* clearLayers)
469 {
470 /* Create new graphic buffer with correct dimensions */
471 mGraphicBuffer = new GraphicBuffer(bufferArea.width, bufferArea.height,
472 mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
473 "hwc2_test_buffer");
474 int ret = mGraphicBuffer->initCheck();
475 if (ret) {
476 return ret;
477 }
478 if (!mGraphicBuffer->handle) {
479 return -EINVAL;
480 }
481
482 uint8_t* img;
483 mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
484
485 uint32_t stride = mGraphicBuffer->getStride();
486
487 float bWDiv3 = bufferArea.width / 3;
488 float bW2Div3 = bufferArea.width * 2 / 3;
489 float bHDiv3 = bufferArea.height / 3;
490 float bH2Div3 = bufferArea.height * 2 / 3;
491
492 /* Cycle through every pixel in the buffer and determine what color it
493 * should be. */
494 for (int32_t y = 0; y < bufferArea.height; y++) {
495 for (int32_t x = 0; x < bufferArea.width; x++) {
496
497 uint8_t r = 0, g = 0, b = 0;
498 float a = 0.0f;
499
500 /* Cycle through each client layer from back to front and
501 * update the pixel color. */
502 for (auto layer = clientLayers->rbegin();
503 layer != clientLayers->rend(); ++layer) {
504
505 const hwc_rect_t df = testLayers->getDisplayFrame(*layer);
506
507 float dfL = df.left;
508 float dfT = df.top;
509 float dfR = df.right;
510 float dfB = df.bottom;
511
512 /* If the pixel location falls outside of the layer display
513 * frame, skip the layer. */
514 if (x < dfL || x >= dfR || y < dfT || y >= dfB)
515 continue;
516
517 /* If the device has requested the layer be clear, clear
518 * the pixel and continue. */
519 if (clearLayers->count(*layer) != 0) {
520 r = 0;
521 g = 0;
522 b = 0;
523 a = 0.0f;
524 continue;
525 }
526
527 float planeAlpha = testLayers->getPlaneAlpha(*layer);
528
529 /* If the layer is a solid color, fill the color and
530 * continue. */
531 if (testLayers->getComposition(*layer)
532 == HWC2_COMPOSITION_SOLID_COLOR) {
533 const auto color = testLayers->getColor(*layer);
534 r = color.r;
535 g = color.g;
536 b = color.b;
537 a = color.a * planeAlpha;
538 continue;
539 }
540
541 float xPos = x;
542 float yPos = y;
543
544 hwc_transform_t transform = testLayers->getTransform(*layer);
545
546 float dfW = dfR - dfL;
547 float dfH = dfB - dfT;
548
549 /* If a layer has a transform, find which location on the
550 * layer will end up in the current pixel location. We
551 * can calculate the color of the current pixel using that
552 * location. */
553 if (transform > 0) {
554 /* Change origin to be the center of the layer. */
555 xPos = xPos - dfL - dfW / 2.0;
556 yPos = yPos - dfT - dfH / 2.0;
557
558 /* Flip Horizontal by reflecting across the y axis. */
559 if (transform & HWC_TRANSFORM_FLIP_H)
560 xPos = -xPos;
561
562 /* Flip vertical by reflecting across the x axis. */
563 if (transform & HWC_TRANSFORM_FLIP_V)
564 yPos = -yPos;
565
566 /* Rotate 90 by using a basic linear algebra rotation
567 * and scaling the result so the display frame remains
568 * the same. For example, a buffer of size 100x50 should
569 * rotate 90 degress but remain the same dimension
570 * (100x50) at the end of the transformation. */
571 if (transform & HWC_TRANSFORM_ROT_90) {
572 float tmp = xPos;
573 xPos = -yPos * dfW / dfH;
574 yPos = tmp * dfH / dfW;
575 }
576
577 /* Change origin back to the top left corner of the
578 * layer. */
579 xPos = xPos + dfL + dfW / 2.0;
580 yPos = yPos + dfT + dfH / 2.0;
581 }
582
583 hwc_frect_t sc = testLayers->getSourceCrop(*layer);
584 float scL = sc.left, scT = sc.top;
585
586 float dfWDivScW = dfW / (sc.right - scL);
587 float dfHDivScH = dfH / (sc.bottom - scT);
588
589 float max = 255, min = 0;
590
591 /* Choose the pixel color. Similar to generateBuffer,
592 * each layer will be divided into 3x3 colors. Because
593 * both the source crop and display frame must be taken into
594 * account, the formulas are more complicated.
595 *
596 * If the source crop and display frame were not taken into
597 * account, we would simply divide the buffer into three
598 * sections by height. Each section would get one color.
599 * For example the formula for the first section would be:
600 *
601 * if (yPos < bufferArea.height / 3)
602 * //Select first section color
603 *
604 * However the pixel color is chosen based on the source
605 * crop and displayed based on the display frame.
606 *
607 * If the display frame top was 0 and the source crop height
608 * and display frame height were the same. The only factor
609 * would be the source crop top. To calculate the new
610 * section boundary, the section boundary would be moved up
611 * by the height of the source crop top. The formula would
612 * be:
613 * if (yPos < (bufferArea.height / 3 - sourceCrop.top)
614 * //Select first section color
615 *
616 * If the display frame top could also vary but source crop
617 * and display frame heights were the same, the formula
618 * would be:
619 * if (yPos < (bufferArea.height / 3 - sourceCrop.top
620 * + displayFrameTop)
621 * //Select first section color
622 *
623 * If the heights were not the same, the conversion between
624 * the source crop and display frame dimensions must be
625 * taken into account. The formula would be:
626 * if (yPos < ((bufferArea.height / 3) - sourceCrop.top)
627 * * displayFrameHeight / sourceCropHeight
628 * + displayFrameTop)
629 * //Select first section color
630 */
631 if (yPos < ((bHDiv3) - scT) * dfHDivScH + dfT) {
632 min = 255 / 2;
633 } else if (yPos >= ((bH2Div3) - scT) * dfHDivScH + dfT) {
634 max = 255 / 2;
635 }
636
637 uint8_t rCur = min, gCur = min, bCur = min;
638 float aCur = 1.0f;
639
640 /* This further divides the color sections from 3 to 3x3.
641 * The math behind it follows the same logic as the previous
642 * comment */
643 if (xPos < ((bWDiv3) - scL) * (dfWDivScW) + dfL) {
644 rCur = max;
645 } else if (xPos < ((bW2Div3) - scL) * (dfWDivScW) + dfL) {
646 gCur = max;
647 } else {
648 bCur = max;
649 }
650
651
652 /* Blend the pixel color with the previous layers' pixel
653 * colors using the plane alpha and blend mode. The final
654 * pixel color is chosen using the plane alpha and blend
655 * mode formulas found in hwcomposer2.h */
656 hwc2_blend_mode_t blendMode = testLayers->getBlendMode(*layer);
657
658 if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
659 rCur *= planeAlpha;
660 gCur *= planeAlpha;
661 bCur *= planeAlpha;
662 }
663
664 aCur *= planeAlpha;
665
666 if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
667 r = rCur + r * (1.0 - aCur);
668 g = gCur + g * (1.0 - aCur);
669 b = bCur + b * (1.0 - aCur);
670 a = aCur + a * (1.0 - aCur);
671 } else if (blendMode == HWC2_BLEND_MODE_COVERAGE) {
672 r = rCur * aCur + r * (1.0 - aCur);
673 g = gCur * aCur + g * (1.0 - aCur);
674 b = bCur * aCur + b * (1.0 - aCur);
675 a = aCur * aCur + a * (1.0 - aCur);
676 } else {
677 r = rCur;
678 g = gCur;
679 b = bCur;
680 a = aCur;
681 }
682 }
683
684 /* Set the pixel color */
685 setColor(x, y, mFormat, stride, img, r, g, b, a * 255);
686 }
687 }
688
689 mGraphicBuffer->unlock();
690
691 *outFence = mFenceGenerator->get();
692 *outHandle = mGraphicBuffer->handle;
693
694 return 0;
695 }
696