1.截屏缩小压缩,减小像素采样的优化算法。默认截屏后缩小到原来的1/4。
2.C++代码进行一次性高斯模糊。避免使用shader造成的渲染掉帧
以下是C++部分代码:
- /*
- * 高斯模糊接口 缩放因子:iScale,截图会把全屏压缩为1/iScale大
- */
- static void gaussianBlur(const std::function<void(bool,cocos2d::Image*)>& afterCaptured,int iScale = 4);
- // The Stack Blur Algorithm was invented by Mario Klingemann,// mario@quasimondo.com and described here:
- // http://incubator.quasimondo.com/processing/fast_blur_deluxe.PHP
- // This is C++ RGBA (32 bit color) multi-threaded version
- // by Victor Laskin (victor.laskin@gmail.com)
- // More details: http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp
- // This code is using MVThread class from my cross-platform framework
- // You can exchange it with any thread implementation you like
- // -------------------------------------- stackblur ----------------------------------------->
- static unsigned short const stackblur_mul[255] =
- {
- 512,512,456,328,335,405,271,388,292,454,364,298,496,420,360,312,273,482,428,383,345,284,259,475,437,404,374,347,323,302,282,265,497,468,441,417,394,373,354,337,320,305,291,278,507,485,465,446,412,396,381,367,341,329,318,307,297,287,269,261,505,489,461,447,435,422,411,399,389,378,368,359,350,332,324,316,309,301,294,281,274,268,262,257,501,491,480,470,460,451,442,433,424,416,408,400,392,385,377,370,363,357,344,338,326,315,310,304,299,289,285,280,275,267,263,259
- };
- static unsigned char const stackblur_shr[255] =
- {
- 9,11,12,13,14,15,16,17,18,19,20,21,22,23,24,24
- };
- /// Stackblur algorithm body
- void stackblurJob(unsigned char* src,///< input image data
- unsigned int w,///< image width
- unsigned int h,///< image height
- unsigned int radius,///< blur intensity (should be in 2..254 range)
- int cores,///< total number of working threads
- int core,///< current thread number
- int step,///< step of processing (1,2)
- unsigned char* stack ///< stack buffer
- )
- {
- unsigned int x,y,xp,yp,i;
- unsigned int sp;
- unsigned int stack_start;
- unsigned char* stack_ptr;
- unsigned char* src_ptr;
- unsigned char* dst_ptr;
- unsigned long sum_r;
- unsigned long sum_g;
- unsigned long sum_b;
- unsigned long sum_a;
- unsigned long sum_in_r;
- unsigned long sum_in_g;
- unsigned long sum_in_b;
- unsigned long sum_in_a;
- unsigned long sum_out_r;
- unsigned long sum_out_g;
- unsigned long sum_out_b;
- unsigned long sum_out_a;
- unsigned int wm = w - 1;
- unsigned int hm = h - 1;
- unsigned int w4 = w * 4;
- unsigned int div = (radius * 2) + 1;
- unsigned int mul_sum = stackblur_mul[radius];
- unsigned char shr_sum = stackblur_shr[radius];
- if (step == 1)
- {
- int minY = core * h / cores;
- int maxY = (core + 1) * h / cores;
- for (y = minY; y < maxY; y++)
- {
- sum_r = sum_g = sum_b = sum_a =
- sum_in_r = sum_in_g = sum_in_b = sum_in_a =
- sum_out_r = sum_out_g = sum_out_b = sum_out_a = 0;
- src_ptr = src + w4 * y; // start of line (0,y)
- for (i = 0; i <= radius; i++)
- {
- stack_ptr = &stack[4 * i];
- stack_ptr[0] = src_ptr[0];
- stack_ptr[1] = src_ptr[1];
- stack_ptr[2] = src_ptr[2];
- stack_ptr[3] = src_ptr[3];
- sum_r += src_ptr[0] * (i + 1);
- sum_g += src_ptr[1] * (i + 1);
- sum_b += src_ptr[2] * (i + 1);
- sum_a += src_ptr[3] * (i + 1);
- sum_out_r += src_ptr[0];
- sum_out_g += src_ptr[1];
- sum_out_b += src_ptr[2];
- sum_out_a += src_ptr[3];
- }
- for (i = 1; i <= radius; i++)
- {
- if (i <= wm) src_ptr += 4;
- stack_ptr = &stack[4 * (i + radius)];
- stack_ptr[0] = src_ptr[0];
- stack_ptr[1] = src_ptr[1];
- stack_ptr[2] = src_ptr[2];
- stack_ptr[3] = src_ptr[3];
- sum_r += src_ptr[0] * (radius + 1 - i);
- sum_g += src_ptr[1] * (radius + 1 - i);
- sum_b += src_ptr[2] * (radius + 1 - i);
- sum_a += src_ptr[3] * (radius + 1 - i);
- sum_in_r += src_ptr[0];
- sum_in_g += src_ptr[1];
- sum_in_b += src_ptr[2];
- sum_in_a += src_ptr[3];
- }
- sp = radius;
- xp = radius;
- if (xp > wm) xp = wm;
- src_ptr = src + 4 * (xp + y * w); // img.pix_ptr(xp,y);
- dst_ptr = src + y * w4; // img.pix_ptr(0,y);
- for (x = 0; x < w; x++)
- {
- dst_ptr[0] = (sum_r * mul_sum) >> shr_sum;
- dst_ptr[1] = (sum_g * mul_sum) >> shr_sum;
- dst_ptr[2] = (sum_b * mul_sum) >> shr_sum;
- dst_ptr[3] = (sum_a * mul_sum) >> shr_sum;
- dst_ptr += 4;
- sum_r -= sum_out_r;
- sum_g -= sum_out_g;
- sum_b -= sum_out_b;
- sum_a -= sum_out_a;
- stack_start = sp + div - radius;
- if (stack_start >= div) stack_start -= div;
- stack_ptr = &stack[4 * stack_start];
- sum_out_r -= stack_ptr[0];
- sum_out_g -= stack_ptr[1];
- sum_out_b -= stack_ptr[2];
- sum_out_a -= stack_ptr[3];
- if (xp < wm)
- {
- src_ptr += 4;
- ++xp;
- }
- stack_ptr[0] = src_ptr[0];
- stack_ptr[1] = src_ptr[1];
- stack_ptr[2] = src_ptr[2];
- stack_ptr[3] = src_ptr[3];
- sum_in_r += src_ptr[0];
- sum_in_g += src_ptr[1];
- sum_in_b += src_ptr[2];
- sum_in_a += src_ptr[3];
- sum_r += sum_in_r;
- sum_g += sum_in_g;
- sum_b += sum_in_b;
- sum_a += sum_in_a;
- ++sp;
- if (sp >= div) sp = 0;
- stack_ptr = &stack[sp * 4];
- sum_out_r += stack_ptr[0];
- sum_out_g += stack_ptr[1];
- sum_out_b += stack_ptr[2];
- sum_out_a += stack_ptr[3];
- sum_in_r -= stack_ptr[0];
- sum_in_g -= stack_ptr[1];
- sum_in_b -= stack_ptr[2];
- sum_in_a -= stack_ptr[3];
- }
- }
- }
- // step 2
- if (step == 2)
- {
- int minX = core * w / cores;
- int maxX = (core + 1) * w / cores;
- for (x = minX; x < maxX; x++)
- {
- sum_r = sum_g = sum_b = sum_a =
- sum_in_r = sum_in_g = sum_in_b = sum_in_a =
- sum_out_r = sum_out_g = sum_out_b = sum_out_a = 0;
- src_ptr = src + 4 * x; // x,0
- for (i = 0; i <= radius; i++)
- {
- stack_ptr = &stack[i * 4];
- stack_ptr[0] = src_ptr[0];
- stack_ptr[1] = src_ptr[1];
- stack_ptr[2] = src_ptr[2];
- stack_ptr[3] = src_ptr[3];
- sum_r += src_ptr[0] * (i + 1);
- sum_g += src_ptr[1] * (i + 1);
- sum_b += src_ptr[2] * (i + 1);
- sum_a += src_ptr[3] * (i + 1);
- sum_out_r += src_ptr[0];
- sum_out_g += src_ptr[1];
- sum_out_b += src_ptr[2];
- sum_out_a += src_ptr[3];
- }
- for (i = 1; i <= radius; i++)
- {
- if (i <= hm) src_ptr += w4; // +stride
- stack_ptr = &stack[4 * (i + radius)];
- stack_ptr[0] = src_ptr[0];
- stack_ptr[1] = src_ptr[1];
- stack_ptr[2] = src_ptr[2];
- stack_ptr[3] = src_ptr[3];
- sum_r += src_ptr[0] * (radius + 1 - i);
- sum_g += src_ptr[1] * (radius + 1 - i);
- sum_b += src_ptr[2] * (radius + 1 - i);
- sum_a += src_ptr[3] * (radius + 1 - i);
- sum_in_r += src_ptr[0];
- sum_in_g += src_ptr[1];
- sum_in_b += src_ptr[2];
- sum_in_a += src_ptr[3];
- }
- sp = radius;
- yp = radius;
- if (yp > hm) yp = hm;
- src_ptr = src + 4 * (x + yp * w); // img.pix_ptr(x,yp);
- dst_ptr = src + 4 * x; // img.pix_ptr(x,0);
- for (y = 0; y < h; y++)
- {
- dst_ptr[0] = (sum_r * mul_sum) >> shr_sum;
- dst_ptr[1] = (sum_g * mul_sum) >> shr_sum;
- dst_ptr[2] = (sum_b * mul_sum) >> shr_sum;
- dst_ptr[3] = (sum_a * mul_sum) >> shr_sum;
- dst_ptr += w4;
- sum_r -= sum_out_r;
- sum_g -= sum_out_g;
- sum_b -= sum_out_b;
- sum_a -= sum_out_a;
- stack_start = sp + div - radius;
- if (stack_start >= div) stack_start -= div;
- stack_ptr = &stack[4 * stack_start];
- sum_out_r -= stack_ptr[0];
- sum_out_g -= stack_ptr[1];
- sum_out_b -= stack_ptr[2];
- sum_out_a -= stack_ptr[3];
- if (yp < hm)
- {
- src_ptr += w4; // stride
- ++yp;
- }
- stack_ptr[0] = src_ptr[0];
- stack_ptr[1] = src_ptr[1];
- stack_ptr[2] = src_ptr[2];
- stack_ptr[3] = src_ptr[3];
- sum_in_r += src_ptr[0];
- sum_in_g += src_ptr[1];
- sum_in_b += src_ptr[2];
- sum_in_a += src_ptr[3];
- sum_r += sum_in_r;
- sum_g += sum_in_g;
- sum_b += sum_in_b;
- sum_a += sum_in_a;
- ++sp;
- if (sp >= div) sp = 0;
- stack_ptr = &stack[sp * 4];
- sum_out_r += stack_ptr[0];
- sum_out_g += stack_ptr[1];
- sum_out_b += stack_ptr[2];
- sum_out_a += stack_ptr[3];
- sum_in_r -= stack_ptr[0];
- sum_in_g -= stack_ptr[1];
- sum_in_b -= stack_ptr[2];
- sum_in_a -= stack_ptr[3];
- }
- }
- }
- }
- class MVImageUtilsStackBlurTask
- {
- public:
- unsigned char* src;
- unsigned int w;
- unsigned int h;
- unsigned int radius;
- int cores;
- int core;
- int step;
- unsigned char* stack;
- inline MVImageUtilsStackBlurTask(unsigned char* src,unsigned int w,unsigned int h,unsigned int radius,int cores,int core,int step,unsigned char* stack)
- {
- this->src = src;
- this->w = w;
- this->h = h;
- this->radius = radius;
- this->cores = cores;
- this->core = core;
- this->step = step;
- this->stack = stack;
- }
- inline void run()
- {
- stackblurJob(src,w,h,radius,cores,core,step,stack);
- }
- };
- /// Stackblur algorithm by Mario Klingemann
- /// Details here:
- /// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
- /// C++ implemenation base from:
- /// https://gist.github.com/benjamin9999/3809142
- /// http://www.antigrain.com/__code/include/agg_blur.h.html
- /// This version works only with RGBA color
- void stackblur(unsigned char* src,///< blur intensity (should be in 2..254 range)
- int cores = 1 ///< number of threads (1 - normal single thread)
- )
- {
- if (radius > 254) return;
- if (radius < 2) return;
- unsigned int div = (radius * 2) + 1;
- unsigned char* stack = new unsigned char[div * 4 * cores];
- if (cores == 1)
- {
- // no multithreading
- stackblurJob(src,1,stack);
- stackblurJob(src,2,stack);
- }
- delete[] stack;
- }
- /**
- * Capture screen implementation,don't use it directly.
- */
- void onCaptureScreen(const std::function<void(bool,Image*)>& afterCaptured,int iScale)
- {
- static bool startedCapture = false;
- if (startedCapture)
- {
- CCLOG("Screen capture is already working");
- if (afterCaptured)
- {
- afterCaptured(false,nullptr);
- }
- return;
- }
- else
- {
- startedCapture = true;
- }
- auto glView = Director::getInstance()->getOpenGLView();
- auto frameSize = glView->getFrameSize();
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
- frameSize = frameSize * glView->getFrameZoomFactor() * glView->getRetinaFactor();
- #endif
- int width = static_cast<int>(frameSize.width);
- int height = static_cast<int>(frameSize.height);
- do
- {
- std::shared_ptr<GLubyte> buffer(new GLubyte[width * height * 4],[](GLubyte* p){ CC_SAFE_DELETE_ARRAY(p); });
- if (!buffer)
- {
- break;
- }
- glPixelStorei(GL_PACK_ALIGNMENT,1);
- glReadPixels(0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,buffer.get());
- std::shared_ptr<GLubyte> flippedBuffer(new GLubyte[width * height * 4],[](GLubyte* p) { CC_SAFE_DELETE_ARRAY(p); });
- if (!flippedBuffer)
- {
- break;
- }
- for (int row = 0; row < height; ++row)
- {
- memcpy(flippedBuffer.get() + (height - row - 1) * width * 4,buffer.get() + row * width * 4,width * 4);
- }
- /*-------------压缩start------------*/
- unsigned long dst_width = width / iScale;
- unsigned long dst_height = height / iScale;
- std::shared_ptr<GLubyte> zipFlippedBuffer(new GLubyte[dst_width * dst_height * 4],[](GLubyte* p) { CC_SAFE_DELETE_ARRAY(p); });
- if (!zipFlippedBuffer)
- {
- break;
- }
- unsigned long xrIntFloat_16 = (width << 16) / dst_width + 1;
- unsigned long yrIntFloat_16 = (height << 16) / dst_height + 1;
- unsigned long srcy_16 = 0;
- unsigned long byte_width = 4;//单个数据的物理宽度 4字节
- unsigned long byte_shift = 2;//单个数据的物理移位
- auto beginPos = zipFlippedBuffer.get();
- for (unsigned long y = 0; y < dst_height; ++y)
- {
- //auto pSrcLine = flippedBuffer.get() + width * byte_width * (srcy_16 >> 16);
- auto pSrcLine = flippedBuffer.get() + (width<<2)*(srcy_16>>16);
- unsigned long srcx_16 = 0;
- for (unsigned long x = 0; x < dst_width; ++x)
- {
- //memcpy(beginPos + x * byte_width,pSrcLine + (srcx_16 >> 16)*byte_width,byte_width);
- memcpy(beginPos + (x<<2),pSrcLine + ((srcx_16 >> 16)<<2),byte_width);
- srcx_16 += xrIntFloat_16;
- }
- srcy_16 += yrIntFloat_16;
- beginPos += (dst_width << byte_shift);
- }
- /*-------------压缩end------------*/
- //使用算法一次性对图片进行高斯模糊
- stackblur(zipFlippedBuffer.get(),dst_width,dst_height,5);
- Image* image = new (std::nothrow) Image;
- if (image)
- {
- image->initWithRawData(zipFlippedBuffer.get(),dst_width * dst_height * 4,8);
- image->autorelease();
- if (afterCaptured)
- {
- afterCaptured(true,image);
- }
- }
- else
- {
- CCLOG("Malloc Image memory Failed!");
- if (afterCaptured)
- {
- afterCaptured(false,nullptr);
- }
- delete image;
- image = nullptr;
- }
- startedCapture = false;
- } while (0);
- }
- /*
- * 高斯模糊接口 缩放因子:iScale,截图会把全屏压缩为1/iScale大
- */
- static EventListenerCustom* s_captureScreenListener;
- static CustomCommand s_captureScreenCommand;
- void Util::gaussianBlur(const std::function<void(bool,int iScale /*= 4*/)
- {
- if (s_captureScreenListener)
- {
- CCLOG("Warning: CaptureScreen has been called already,don't call more than once in one frame.");
- return;
- }
- s_captureScreenCommand.init(std::numeric_limits<float>::max());
- s_captureScreenCommand.func = std::bind(onCaptureScreen,afterCaptured,iScale);
- s_captureScreenListener = Director::getInstance()->getEventDispatcher()->addCustomEventListener(Director::EVENT_AFTER_DRAW,[](EventCustom *event) {
- auto director = Director::getInstance();
- director->getEventDispatcher()->removeEventListener((EventListener*)(s_captureScreenListener));
- s_captureScreenListener = nullptr;
- director->getRenderer()->addCommand(&s_captureScreenCommand);
- director->getRenderer()->render();
- });
- }
以下是导出的lua接口:
- #include "base/ccConfig.h"
- #ifndef __game_custom_h__
- #define __game_custom_h__
- #ifdef __cplusplus
- extern "C" {
- #endif
- #include "tolua++.h"
- #ifdef __cplusplus
- }
- #endif
- int register_all_game_custom(lua_State* tolua_S);
- #endif // __game_custom_h__
- static int tolua_pf_common_gaussianBlur(lua_State* tolua_S)
- {
- LUA_FUNCTION callbackHander = toluafix_ref_function(tolua_S,0);
- if (callbackHander == 0)
- {
- CCLOG("tolua_pf_common_gaussianBlur : toluafix_ref_function,error");
- return 0;
- }
- auto capture_callback = [=](bool succeed,Image* img){
- auto luastack = LuaEngine::getInstance()->getLuaStack();
- luastack->pushBoolean(succeed);
- if (succeed){
- luastack->pushObject(img,"cc.Image");
- }
- else{
- luastack->pushNil();
- }
- luastack->executeFunctionByHandler(callbackHander,2);
- };
- int argc = lua_gettop(tolua_S) - 1;
- if (argc == 2)
- {
- int q = 4;
- if (!luaval_to_int32(tolua_S,3,&q))
- {
- CCLOG("tolua_pf_common_gaussianBlur : luaval_to_number,error");
- return 0;
- }
- Util::gaussianBlur(capture_callback,q);
- }
- else
- {
- Util::gaussianBlur(capture_callback);
- }
- return 0;
- }
- TOLUA_API int register_all_game_custom(lua_State* tolua_S)
- {
- tolua_open(tolua_S);
- tolua_module(tolua_S,"pf",0);
- tolua_beginmodule(tolua_S,"pf");
- tolua_module(tolua_S,"Common",0);
- tolua_beginmodule(tolua_S,"Common");
- {
- tolua_function(tolua_S,"GaussianBlur",tolua_pf_common_gaussianBlur);
- }
- tolua_endmodule(tolua_S);
- tolua_endmodule(tolua_S);
- return 1;
- }
使用方法:
- local function onFinishCapture(ret,img)
- if ret then
- local texture = cc.Director:getInstance():getTextureCache():addImage(img,"capriteadu")
- local spriteBlur = cc.Sprite:createWithTexture(texture)
- local wSize = cc.Director:getInstance():getWinSize()
- spriteBlur:setPosition(cc.p(wSize.width/2,wSize.height/2))
- self:addChild(spriteBlur)
- PF.UIEx.nodeToScaleForFixedSize(spriteBlur,wSize)
- end
- end
- pf.Common:GaussianBlur(onFinishCapture,4)