精华内容
下载资源
问答
  • 做了一个OSG离屏渲染,即将OSG渲染结果的图片纹理提取出来,提取到OpenCV并转化为Mat类型来显示,便于后续操作,还是比较有价值的工作。其中模型是动态的模型。 OSG 离屏渲染 上面这个视频中(b站链接在这里)...

    代码效果

    做了一个OSG离屏渲染,即将OSG渲染结果的图片纹理提取出来,提取到OpenCV并转化为Mat类型来显示,便于后续操作,还是比较有价值的工作。其中模型是动态的模型。

    OSG 离屏渲染

    上面这个视频中(b站链接在这里),左边是调用viewer.frame()进行渲染时自动显示的图像,是反的,这个无所谓了,不是我们要用的东西;右边是我将纹理提取到OpenCV的Mat中然后用imshow展示出来的,是便于在OpenCV中做进一步处理的。

    其实还是比较难做的,具体解释和做法详见下文。

    主要难点

    参考了很多资料,但是多数资料都只能把渲染后的图片纹理直接保存到本地,主要有两种思路:

    • 调用attach将OSG的camera与image连接起来,image就可以导出纹理到本地;
    • 读viewer的缓存,转化为图像。

    然而OSG有一个特性,即OSG中camera看到的图像是上下颠倒的,并且存储camera看到的图像的内存是会实时刷新的,这给离屏渲染造成了很大困扰,以上两种方法保存到本地都没有问题,但是如果要实时纹理提取,得到的纹理就是上下颠倒的。

    如果将得到的纹理再做一次上下反转,你会惊奇的发现你得到的纹理一直在闪烁。这是因为缓存是不断刷新的,总会有一部分被下一帧(颠倒的)覆盖掉。

    解决方案

    解决方案就是反向渲染,即进行渲染前先把背景反转一下。模型也反向渲染。
    当然还有一种取巧的办法,就是渲染时不上传背景图,只把模型渲染到默认幕布北京上,然后把幕布替换为我们的背景图。这种方法比较好写。本文走的是比较复杂的路子。。

    代码

    环境:win10, vs2015, opencv3.4.13, OSG3.6.5
    代码结构如下图:
    在这里插入图片描述
    其中 modelrend.h为:

    #pragma once
    
    #include <windows.h>
    #include <osg/Camera>
    #include <osg/PolygonMode>
    #include <osg/Texture2D>
    #include <osg/Geode>
    #include <osg/Geometry>
    
    #include <osgViewer/Viewer>
    #include <osgDB/ReadFile>
    #include <osgDB/WriteFile>
    #include <osg/PositionAttitudeTransform>
    #include <osgAnimation/BasicAnimationManager>         
    
    class VirtualCamera {
    public:
    	void createVirtualCamera(osg::ref_ptr<osg::Camera> cam, int width, int height);
    	void updatePosition(double r, double p, double h, double x, double y, double z);
    
    	double angle;
    	osg::Matrix rotation;
    	osg::Matrix translation;
    	osg::ref_ptr<osg::Camera> camera;
    	osg::ref_ptr<osg::Image> image;
    };
    
    class BackgroundCamera {
    public:
    	BackgroundCamera();
    	void update(uint8_t* data, int cols, int rows);
    	osg::Geode* createCameraPlane(int textureWidth, int textureHeight);
    	osg::Camera* createCamera(int textureWidth, int textureHeight);
    
    	osg::ref_ptr<osg::Image> img;
    };
    
    class Modelrender {
    public:
    	osgViewer::Viewer viewer;
    	BackgroundCamera bgCamera;
    	VirtualCamera vCamera;
    	double angleRoll;
    	int width, height;
    
    	Modelrender(int cols, int rows);
    	uint8_t* rend(uint8_t* inputimag);
    };
    
    

    modelrend.cpp为:

    #include "modelrend.h"
    
    #include <windows.h>
    #include <osgViewer/Viewer>
    #include <osgDB/ReadFile>
    #include <osgDB/WriteFile>
    #include <osg/PositionAttitudeTransform>
    #include <osgAnimation/BasicAnimationManager>
    
    #include <osgViewer/GraphicsWindow>
    #include <osg/Node>
    #include <osg/Geode>
    #include <osg/Group>
    #include <osg/Camera>
    #include <osg/Image>
    #include <osg/BufferObject>
    #include <osgUtil/Optimizer>
    #include <osgGA/GUIEventHandler>
    #include <osgGA/TrackballManipulator>
    
    osg::ref_ptr<osg::Image> _image;
    
    void VirtualCamera::createVirtualCamera(osg::ref_ptr<osg::Camera> cam, int width, int height)
    {
    	camera = cam;
    	// Initial Values
    	camera->setProjectionMatrixAsPerspective(320, 1., 1., 100.); //角度取了320,角度大于180时为反向渲染模型
    	camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
    	image = new osg::Image;
    	image->allocateImage(width, height, 1, GL_BGR, GL_UNSIGNED_BYTE);
    	camera->attach(osg::Camera::COLOR_BUFFER, image.get());
    }
    
    void VirtualCamera::updatePosition(double r, double p, double h, double x, double y, double z)
    { //模型旋转模块
    	osg::Matrixd myCameraMatrix;
    
    	// Update Rotation
    	rotation.makeRotate(
    		osg::DegreesToRadians(r), osg::Vec3(0, 1, 0), // roll
    		osg::DegreesToRadians(p), osg::Vec3(1, 0, 0), // pitch
    		osg::DegreesToRadians(h), osg::Vec3(0, 0, 1)); // heading
    
    													   // Update Translation
    	translation.makeTranslate(x, y, z);
    	myCameraMatrix = rotation * translation;
    	osg::Matrixd i = myCameraMatrix.inverse(myCameraMatrix);
    	camera->setViewMatrix(i*osg::Matrix::rotate(-(osg::PI_2), 1, -0, 0));
    }
    
    
    
    BackgroundCamera::BackgroundCamera() {
    	// Create OSG Image from CV Mat
    	img = new osg::Image;
    }
    
    void flipImageV(unsigned char* top, unsigned char* bottom, unsigned int rowSize, unsigned int rowStep)
    { //此函数用于在给输入的背景图片做反转
    	while (top<bottom)
    	{
    		unsigned char* t = top;
    		unsigned char* b = bottom;
    		for (unsigned int i = 0; i<rowSize; ++i, ++t, ++b)
    		{
    			unsigned char temp = *t;
    			*t = *b;
    			*b = temp;
    		}
    		top += rowStep;
    		bottom -= rowStep;
    	}
    }
    
    void BackgroundCamera::update(uint8_t* data, int width, int height)
    { //接收输入背景图像并做反转
    	// img->setImage(width, height, 3,
    	// 	GL_RGB, GL_BGR, GL_UNSIGNED_BYTE,
    	// 	data,
    	// 	osg::Image::AllocationMode::NO_DELETE, 1);
    	// img->dirty();
    	unsigned char* top = data;
    	unsigned char* bottom = top + (height - 1)*3*width;
    
    	flipImageV(top, bottom, width*3, width*3);
    
    	img->setImage(width, height, 3,
    		GL_RGB, GL_BGR, GL_UNSIGNED_BYTE,
    		data,
    		osg::Image::AllocationMode::NO_DELETE, 1);
    	img->dirty();
    }
    
    osg::Geode* BackgroundCamera::createCameraPlane(int textureWidth, int textureHeight)
    {
    	// CREATE PLANE TO DRAW TEXTURE
    	osg::ref_ptr<osg::Geometry> quadGeometry = osg::createTexturedQuadGeometry(osg::Vec3(0.0f, 0.0f, 0.0f),
    		osg::Vec3(textureWidth, 0.0f, 0.0f),
    		osg::Vec3(0.0, textureHeight, 0.0),
    		0.0f,
    		1.0f,
    		1.0f,
    		0.0f);
    	// PUT PLANE INTO NODE
    	osg::ref_ptr<osg::Geode> quad = new osg::Geode;
    	quad->addDrawable(quadGeometry);
    	// DISABLE SHADOW / LIGHTNING EFFECTS
    	int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED;
    	quad->getOrCreateStateSet()->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), values);
    	quad->getOrCreateStateSet()->setMode(GL_LIGHTING, values);
    
    	osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
    	texture->setTextureSize(textureWidth, textureHeight);
    	texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
    	texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
    	texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
    	texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
    	texture->setResizeNonPowerOfTwoHint(false);
    
    	texture->setImage(img);
    
    	// Apply texture to quad
    	osg::ref_ptr<osg::StateSet> stateSet = quadGeometry->getOrCreateStateSet();
    	stateSet->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
    
    	return quad.release();
    }
    
    osg::Camera* BackgroundCamera::createCamera(int textureWidth, int textureHeight)
    {
    	osg::ref_ptr<osg::Geode> quad = createCameraPlane(textureWidth, textureHeight);
    	//Bind texture to the quadGeometry, then use the following camera:
    	osg::Camera* camera = new osg::Camera;
    	// CAMERA SETUP
    	camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
    	// use identity view matrix so that children do not get (view) transformed
    	camera->setViewMatrix(osg::Matrix::identity());
    	camera->setClearMask(GL_DEPTH_BUFFER_BIT);
    	camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.0));
    	camera->setProjectionMatrixAsOrtho(0.f, textureWidth, 0.f, textureHeight, 1.0, 500.f);
    	// set resize policy to fixed
    	camera->setProjectionResizePolicy(osg::Camera::ProjectionResizePolicy::FIXED);
    	// we don't want the camera to grab event focus from the viewers main camera(s).
    	camera->setAllowEventFocus(false);
    	// only clear the depth buffer
    	camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	//camera->setViewport( 0, 0, screenWidth, screenHeight );
    	camera->setRenderOrder(osg::Camera::NESTED_RENDER);
    	camera->addChild(quad);
    	return camera;
    }
    
    Modelrender::Modelrender(int cols, int rows)
    {
    	width = cols;
    	height = rows;
    	// OSG STUFF
    	// Create viewer
    	viewer.setUpViewInWindow(50, 50, width, height);
    
    	// Main Camera
    	osg::ref_ptr<osg::Camera>  camera = viewer.getCamera();
    	vCamera.createVirtualCamera(camera, width, height);
    
    	// Background-Camera (OpenCV Feed)
    	osg::Camera* backgroundCamera = bgCamera.createCamera(width, height);
    
    	// Load Truck Model as Example Scene
    	osg::ref_ptr<osg::Node> truckModel = osgDB::readNodeFile("avatar.osg"); //spaceship.osgt; º¬¶¯»­µÄ£º nathan.osg, avatar.osg, bignathan.osg,
    	osg::Group* truckGroup = new osg::Group();
    	// Position of truck
    	osg::PositionAttitudeTransform* position = new osg::PositionAttitudeTransform();
    
    	osgAnimation::BasicAnimationManager* anim =
    		dynamic_cast<osgAnimation::BasicAnimationManager*>(truckModel->getUpdateCallback());
    	const osgAnimation::AnimationList& list = anim->getAnimationList();
    	anim->playAnimation(list[0].get()); //模型动画的调用
    
    	truckGroup->addChild(position);
    	position->addChild(truckModel);
    
    	// Set Position of Model
    	osg::Vec3 modelPosition(0, 100, 0);
    	position->setPosition(modelPosition);
    
    	// Create new group node
    	osg::ref_ptr<osg::Group> group = new osg::Group;
    	osg::Node* background = backgroundCamera;
    	osg::Node* foreground = truckGroup;
    	background->getOrCreateStateSet()->setRenderBinDetails(1, "RenderBin");
    	foreground->getOrCreateStateSet()->setRenderBinDetails(2, "RenderBin");
    	group->addChild(background);
    	group->addChild(foreground);
    	background->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
    	foreground->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
    
    	// Add the groud to the viewer、
    	// _image = new osg::Image();
    	viewer.setSceneData(group.get());
    
    	angleRoll = 0.0;
    }
    
    uint8_t* Modelrender::rend(uint8_t * inputimage)
    {
    	bgCamera.update(inputimage, width, height);
    
    	//angleRoll += 0.5;
    
    	// Position Parameters: Roll, Pitch, Heading, X, Y, Z
    	vCamera.updatePosition(angleRoll, 0, 0, 0, 0, 0);
    	viewer.frame();
    	//vCamera.image->flipVertical();
    	return vCamera.image->data();
    }
    
    

    main.cpp为:

    #include <iostream>
    #include <algorithm>
    
    #include "modelrend.h"
    
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/core/core.hpp"
    
    using namespace std;
    
    int main(int argc, char** argv)
    {
    	//cv::VideoCapture cap("movie.mkv");
    	cv::VideoCapture cap(0); //打开摄像头
    
    	if (!cap.isOpened())
    	{
    		std::cout << "Webcam cannot open!\n";
    		return 0;
    	}
    	int frameH = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
    	int frameW = cap.get(CV_CAP_PROP_FRAME_WIDTH);
    	int fps = cap.get(CV_CAP_PROP_FPS);
    	int numFrames = cap.get(CV_CAP_PROP_FRAME_COUNT);
    	printf("height=%d, width=%d, fps=%d, totalframes=%d", frameH, frameW, fps, numFrames);
    
    	Modelrender render(frameW, frameH);
    
    	while (1)
    	{
    		// Refresh Background Image
    		cv::Mat frame;
    		cap >> frame;
    
    		cv::Mat dst1 = cv::Mat(frame.size(), CV_8UC3, cv::Scalar(255, 255, 255));
    
    		dst1.data = render.rend(frame.data); //将摄像头采集到的图像作为背景输入进行渲染,然后取出纹理到dst1,完成离屏渲染
    
    		cv::imshow("test", dst1); //将dst1显示出来
    		cvWaitKey(5);
    	}
    	return 0;
    }
    
    
    
    展开全文
  • 主要:nuxt.js服务器的渲染配置,在配置中注意的问题以及后台要安装配置哪些技术,和nginx反向代理配置和注意问题
  • 位图渲染 BitmapShader 简介( 1 ) 位图渲染综述 ( ① 三种方式 : Shader.TileMode.CLAMP | Shader.TileMode.REPEAT | Shader.TileMode.MIRROR | ② 流程 : 创建 Shader | 设置 Shader 到 Paint | 打开抗锯齿 | 绘制...



    相关代码地址 :




    1. 位图渲染 BitmapShader 简介



    ( 1 ) 位图渲染综述 ( ① 三种方式 : Shader.TileMode.CLAMP | Shader.TileMode.REPEAT | Shader.TileMode.MIRROR | ② 流程 : 创建 Shader | 设置 Shader 到 Paint | 打开抗锯齿 | 绘制矩形 )


    位图渲染 :

    • 1.主要实现的功能 : 位图渲染就是 将一个位图, 通过特定的方式绘制到指定的矩形区域中, 解决 Bitmap 位图的宽高 与 绘制区域宽高 不一致时如何进行渲染 的 问题;
    • 2.渲染流程 :
      • ① 创建 BitmapShader
      • ② 为 Paint 设置 着色器 Shader
      • ③ 打开抗锯齿
      • ④ 绘制一个矩形区域
    • 3.创建 BitmapShader : 调用 BitmapShader 构造方法创建着色器, 同时 设置 位图引用, 和 绘制位图时 的 X 和 Y 方向的拉伸方式 , 位图的 拉伸方式 在后面有介绍 , 下面是三个参数说明 :
      • ① Bitmap bitmap 参数 : 渲染所用的位图 ;
      • ② TileMode tileX 参数 : 设置绘制位图时的 x 方向的拉伸方式 ;
      • ③ TileMode tileY : 设置绘制位图时的 y 方向的拉伸方式 ;
        /**
         * 调用该构造函数创建一个新的着色器, 用于绘制位图
         *
         * @param bitmap 将要绘制的位图
         * @param tileX 设置绘制位图时的 x 方向的拉伸方式
         * @param tileY 设置绘制位图时的 y 方向的拉伸方式
         */
        public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {
            this(bitmap, tileX.nativeInt, tileY.nativeInt);
        }
    
    • 4.三种位图的拉伸方式 :
      • ① Shader.TileMode.CLAMP : 如果绘制的位置超出了图像的边界, 那么超出部分 使用最后一个像素的颜色值绘制 ;
      • ② Shader.TileMode.REPEAT : 绘图位置超出了边界, 使用 同样的位图进行平铺 剩余绘制的部分;
      • ③ Shader.TileMode.MIRROR : 绘图位置超出了边界, 使用 位图反转镜像 平铺剩余绘制部分;
        在这里插入图片描述
    public class Shader {
        ...
        public enum TileMode {
            /**
             * replicate the edge color if the shader draws outside of its
             * original bounds
             */
            CLAMP   (0),
            /**
             * repeat the shader's image horizontally and vertically
             */
            REPEAT  (1),
            /**
             * repeat the shader's image horizontally and vertically, alternating
             * mirror images so that adjacent images always seam
             */
            MIRROR  (2);
        
            TileMode(int nativeInt) {
                this.nativeInt = nativeInt;
            }
            final int nativeInt;
        }
        ...
    }
    
    • 5.为 画笔 设置 着色器 : 调用 Paint 对象 的 setShader 方法为 画笔设置 着色器;
    • 6.打开抗锯齿 : 调用 Paint 对象的 setAntiAlias 方法, 打开抗锯齿, 这样 位图的边界会更平滑, paint.setAntiAlias(true) ;
    • 7.绘制矩形 : 调用 Canvas 的 drawRect 的方法, 绘制矩形, 位图在该矩形中绘制; canvas.drawRect(new Rect(0,0 , 100, 100),mPaint); ;
    • 8.使用示例 : 下面是位图渲染 的简单示例;
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.WHITE);
    
            Paint mPaint = new Paint();
            Bitmap mBitmap = ((BitmapDrawable)getResources()
                    .getDrawable(R.mipmap.aodesai)).getBitmap();
    
            //1. 创建位图渲染对象, 并设置拉伸方式, 此处设置Shader.TileMode.CLAMP,
            //   如果绘制的位置超出了图像的边界, 那么超出部分 使用最后一个像素的颜色值绘制
            BitmapShader bitmapShader = new BitmapShader(mBitmap,
                    Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    
            //2. 设置渲染到 Paint 对象
            mPaint.setShader(bitmapShader);
    
            //3. 打开抗锯齿
            mPaint.setAntiAlias(true);
    
            //4. 绘制指定的矩形区域
            canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        }
    




    2. 位图渲染 BitmapShader 三种参数 及 代码示例



    ( 1 ) 位图渲染 CLAMP 拉伸 代码示例 及 效果 ( 绘制超出图片边界时, 就会绘制 水平 或 垂直方向 上最后一个像素, 填充剩余的位置 )


    CLAMP 拉伸 :

    • 1.CLAMP 说明 : 在创建 BitmapShader 的时候, 设置其 水平 和 垂直方向的 拉伸方式Shader.TileMode.CLAMP , 则在绘制超出图片边界时, 就会绘制 水平 或 垂直方向 上最后一个像素, 填充剩余的位置 ;
    • 2.展示效果 :
      在这里插入图片描述
    • 2.代码示例 :
    package com.hanshuliang.shader.widget;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.ComposeShader;
    import android.graphics.LinearGradient;
    import android.graphics.Paint;
    import android.graphics.PorterDuff;
    import android.graphics.Shader;
    import android.graphics.drawable.BitmapDrawable;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.view.View;
    
    import com.hanshuliang.shader.R;
    
    
    public class PaintBitmapShaderClamp extends View {
    
        public PaintBitmapShaderClamp(Context context) {
            super(context);
        }
    
        public PaintBitmapShaderClamp(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public PaintBitmapShaderClamp(Context context, @Nullable AttributeSet attrs,
                                      int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.WHITE);
    
            Paint mPaint = new Paint();
            Bitmap mBitmap = ((BitmapDrawable)getResources()
                    .getDrawable(R.mipmap.aodesai)).getBitmap();
    
            //1. 创建位图渲染对象, 并设置拉伸方式, 此处设置Shader.TileMode.CLAMP,
            //   如果绘制的位置超出了图像的边界, 那么超出部分 使用最后一个像素的颜色值绘制
            BitmapShader bitmapShader = new BitmapShader(mBitmap,
                    Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    
            //2. 设置渲染到 Paint 对象
            mPaint.setShader(bitmapShader);
    
            //3. 打开抗锯齿
            mPaint.setAntiAlias(true);
    
            //4. 绘制指定的矩形区域
            canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        }
    }
    
    



    ( 2 ) 位图渲染 REPEAT 拉伸 代码示例 及 效果 ( 绘制超出图片边界时, 就会绘制 同样的图片 填充剩余部分 )


    REPEAT 拉伸 :

    • 1.REPEAT 说明 : 在创建 BitmapShader 的时候, 设置其 水平 和 垂直方向的 拉伸方式为 Shader.TileMode.REPEAT , 则在绘制超出图片边界时, 就会绘制 同样的图片 填充剩余部分 ;
    • 2.展示效果 :
      在这里插入图片描述
    • 2.代码示例 :
    package com.hanshuliang.shader.widget;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Shader;
    import android.graphics.drawable.BitmapDrawable;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.view.View;
    
    import com.hanshuliang.shader.R;
    
    
    public class PaintBitmapShaderRepeat extends View {
    
        public PaintBitmapShaderRepeat(Context context) {
            super(context);
        }
    
        public PaintBitmapShaderRepeat(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public PaintBitmapShaderRepeat(Context context, @Nullable AttributeSet attrs, 
                                       int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.WHITE);
    
            Paint mPaint = new Paint();
            Bitmap mBitmap = ((BitmapDrawable)getResources().
                    getDrawable(R.mipmap.aodesai)).getBitmap();
    
            //1. 创建位图渲染对象, 并设置拉伸方式, 此处设置Shader.TileMode.CLAMP,  
            //   如果绘制的位置超出了图像的边界, 使用平铺方式填充
            BitmapShader bitmapShader = new BitmapShader(mBitmap, 
                    Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
    
            //2. 设置渲染到 Paint 对象
            mPaint.setShader(bitmapShader);
    
            //3. 打开抗锯齿
            mPaint.setAntiAlias(true);
    
            //4. 绘制指定的矩形区域
            canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        }
    }
    
    



    ( 3 ) 位图渲染 MIRROR 拉伸 代码示例 及 效果 ( 在垂直和水平方向绘制图片的对应方向的反向图片 )


    MIRROR 拉伸 :

    • 1.MIRROR 说明 : 在创建 BitmapShader 的时候, 设置其 水平 和 垂直方向的 拉伸方式为 Shader.TileMode.MIRROR , 则在绘制超出图片边界时, 就会绘制 图片的 镜像翻转方式 铺满剩余部分;
    • 2.展示效果 :
      在这里插入图片描述
    • 2.代码示例 :
    package com.hanshuliang.shader.widget;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Shader;
    import android.graphics.drawable.BitmapDrawable;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.view.View;
    
    import com.hanshuliang.shader.R;
    
    
    public class PaintBitmapShaderMirror extends View {
    
        public PaintBitmapShaderMirror(Context context) {
            super(context);
        }
    
        public PaintBitmapShaderMirror(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public PaintBitmapShaderMirror(Context context, @Nullable AttributeSet attrs,
                                       int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.WHITE);
    
            Paint mPaint = new Paint();
            Bitmap mBitmap = ((BitmapDrawable)getResources().
                    getDrawable(R.mipmap.aodesai)).getBitmap();
    
            //1. 创建位图渲染对象, 并设置拉伸方式, 此处设置Shader.TileMode.CLAMP,
            //   如果绘制的位置超出了图像的边界, 那么超出部分 使用镜像平铺方式填充
            BitmapShader bitmapShader = new BitmapShader(mBitmap,
                    Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
    
            //2. 设置渲染到 Paint 对象
            mPaint.setShader(bitmapShader);
    
            //3. 打开抗锯齿
            mPaint.setAntiAlias(true);
    
            //4. 绘制指定的矩形区域
            canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        }
    }
    
    

    相关代码地址 :

    展开全文
  • {% for obj in list reversed %} #反向完成循环。      ③ 遍历字典 {% for key,val in dic.items %} <p>{{ key }}:{{ val }} {% endfor %}    ④循环序号 (必须在循环内部使用) forloop....

    一.语法 

    两种特殊符号(语法):
    
         {{  }}和 {% %}
    
      变量相关的用{{}},逻辑相关的用{%%}。

    二.变量

      1. 可直接用  {{ 变量名 }}  

       (可调用字符串, 数字 ,列表,字典,对象等)

    由 view.py 传到  .html  
    
    1.在 view.py 中
    def base(request):
    
        ls1 = ['第一页','第二页','第三页']
    
       # return render(request,'tags.html',{'ls1':ls1})   # 直传用到的变量. 可提高效率
       return render(request,'tags.html',locals())       #传所有的变量
    2.在 .html  文件中
    
    <p>{{ ls1 }}</p>   #所有传的内容都是以字符串的形式

    2.深度查询据点符( . )

      (数字索引查询, 字典查询,属性和方法查询)

    在view.py 文件中
    def index(request):
        import datetime
        s = "hello"
        l = [111, 222, 333]  # 列表
        dic = {"name": "yuan", "age": 18}  # 字典
        date = datetime.date(1993, 5, 2)  # 日期对象
    
        class Person(object):
            def __init__(self, name):
                self.name = name
            def dream(self):
                return 'dreamer'
        person_yuan = Person("chao")  # 自定义类对象
        person_egon = Person("yantao")
        person_alex = Person("jinxin")
    
        person_list = [person_yuan, person_egon, person_alex]
    
        return render(request, "index.html", {"l": l, "dic": dic, "date": date, "person_list": person_list})
        # return render(request,'index.html',locals())
        #locals()获取函数内容所有的变量,然后通过render方法给了index.html文件进行模板渲染,
        如果你图省事,你可以用它,但是很多多余的变量也被传进去了,效率低
    在 .html 文件中
    <h4>{{s}}</h4>
    <h4>列表:{{ l.0 }}</h4>
    <h4>列表:{{ l.2 }}</h4>
    <h4>字典:{{ dic.name }}</h4>
    <h4>日期:{{ date.year }}</h4>
    
    <!--取列表的第1个对象的name属性的值-->
    <h4>类对象列表:{{ person_list.0.name }}</h4>
    <!--取列表的第1个对象的dream方法的返回值,如果没有返回值,拿到的是none-->
    <h4>类对象列表:{{ person_list.0.dream }}</h4>
    注意:
        调用对象里面的方法的时候,不需要写括号来执行,并且只能执行不需要传参数的方法,
        如果你的这个方法需要传参数,那么模板语言不支持,不能帮你渲染

    三.过滤器

      1.语法

     {{ value|方法:参数 }}

      2.常用方法

      ① lower :将文本全部变成小写  

    {{ name|lower }}

      ②join :可以连接一个列表中的元素

    {{ list|join:'+' }}

      ③default: 如果一个变量是false或者为空,使用给定的默认值。

               否则,使用变量的值。 

    {{ value|default:"nothing"}}

      ④length: 返回值的长度,作用于字符串和列表

    {{ value|length }}

     

      ⑤filesizeformat : 将值 格式化为一个"人类可读的"文件尺寸

        (如果 value 是 123456789,输出将会是 117.7 MB。)

    {{ value|filesizeformat }}

     

      ⑥slice:切片

    # value="hello world"

    {{value|slice:"2:-1"}}

     

      ⑦date  格式化

    #value=datetime.datetime.now()

    {{ value|date:"Y-m-d H:i:s"}}

     

      ⑧safe : 可识别 标签 等

     #value = "<a href='#'>点我</a>" 

    {{ value|safe}}

     

      ⑨truncatechars:如果 字符串字符 多于指定的 字符 数量,那么会被截断。

                截断的字符串将以可翻译的省略号序列(“...”)结尾。

    {{ value|truncatechars:9}} #注意:最后那三个省略号也是9个字符里面的,也就是这个9截断出来的是6个字符+3个省略号,
         参数:截断的字符数

     

      ⑩truncatewords:在一定数量的字后截断字符串,是截多少个单词。

    例如:‘hello girl hi baby yue ma’,

    {{ value|truncatewords:3}}  

    #上面例子得到的结果是 'hello girl h1...'

     

      ⑪cut: 移除 参数指定的字符串 

    {{ value|cut:' ' }}

     

    四.标签

      1. for 标签

      ①

    {% for person in person_list %}
        <p>{{ person.name }}</p>  <!--凡是变量都要用两个大括号括起来-->
    {% endfor %}

     

      

    {% for obj in list reversed %}  #反向完成循环。

     

      ③ 遍历字典

    {% for key,val in dic.items %}
        <p>{{ key }}:{{ val }}</p>
    {% endfor %}

     

      ④循环序号 (必须在循环内部使用)

    forloop.counter            当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
    forloop.counter0           当前循环的索引值(从0开始)
    forloop.revcounter         当前循环的倒序索引值(从1开始)
    forloop.revcounter0        当前循环的倒序索引值(从0开始)
    forloop.first              当前循环是不是第一次循环(布尔值)
    forloop.last               当前循环是不是最后一次循环(布尔值)
    forloop.parentloop.counter  本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等

     

    
    

    ⑤ for.....empty

    
    

           for 标签带有一个可选的{% empty %} 从句,

    
    

          以便在给出的组是空的或者没有被找到时,可以有所操作。

    {% for person in person_list %}
        <p>{{ person.name }}</p>
    
    {% empty %}
        <p>sorry,no person here</p>
    {% endfor %}

     2. if 标签   

    if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。

     

    
    
    {% if num > 100 or num < 0 %}
        <p>无效</p>  <!--不满足条件,不会生成这个标签-->
    {% elif num > 80 and num < 100 %}
        <p>优秀</p>
    {% else %}  <!--也是在if标签结构里面的-->
        <p>凑活吧</p>
    {% endif %}
    
    

      3.with 标签

    使用一个简单地名字缓存一个复杂的变量,多用于给一个复杂的变量起别名,
      当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的

     

    {% with total=business.employees.count %}  #等号 两边没有空格
        {{ total }} <!--只能在with语句体内用-->
    {% endwith %}

     

     

       4. csrf_token 标签

    复制代码
    这个标签用于跨站请求伪造保护,
    
        在页面的form表单里面(注意是在form表单里面)任何位置写上

    {% csrf_token %},

    这个东西模板渲染的时候替换成了

    <input type="hidden" name="csrfmiddlewaretoken"
    value="8J4z1wiUEXt0gJSN59dLMnktrXFW0hv7m4d40Mtl37D7vJZfrxLir9L3jSTDjtG8">,
    隐藏的,这个标签的值是个随机字符串,提交的时候,这个东西也被提交了
    复制代码

     

     

     

     

     五.模板继承

    Django模版引擎中最强大也是最复杂的部分就是模版继承了。
      模版继承可以让您创建一个基本的“骨架”模版,
      它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。

     

       1.

      母模板 ,html  内容

     

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <link rel="stylesheet" href="style.css" />
        <title>{% block title %}My amazing site{%/span> endblock %}</title>  #可继承一
    </head>
    
    <body>
        <div id="sidebar">
            {% block sidebar %}  # 可继承二
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog/">Blog</a></li>
            </ul>
            {% endblock %}
        </div>
    
        <div id="content">
            {% block content %}{% endblock %}  #可继承三
        </div>
    </body>
    </html>
    View Code

     

    子模板内容  ( 修改 可继承 的内容)

     

    {% extends "base.html" %}   #继承的关键  引入父模板
     
    {% block title %}My amazing blog{% endblock %}  #继承一
     
    {% block content %}     继承三
    {% for entry in blog_entries %}
        <h2>{{ entry.title }}</h2>
        <p>{{ entry.body }}</p>
    {% endfor %}
    {% endblock %}
    
    
    #没有写 继承二  ,直接用母模板中的内容

     

     

     

       2. 既保留母模板的内容 ,又要子模板 的内

     

     

    六.  FBV 和CBV

      1.FBV(function base views) 

        就是在视图里使用函数处理请求

        (之前都是FBV模式)

      2.CBV(class base views)

        就是在视图里使用类处理请求

        ①基本形式

    # urls.py  文件中
    from django.conf.urls import url
    from app01 import views #引入我们在views.py里面创建的类
      
    urlpatterns = [
         url(r'^index/$', views.MyView.as_view()),
    ]
    
    
    
    #view.py 文件中
      from django.http import HttpResponse
      from django.views import View
    
      class MyView(View):
    
        def get(self, request):    #如果请求方式为 GET 方式
          return HttpResponse('OK')
    View Code

     

    ② CBV传参

     

    #url中的写法
    
     url(r'^cv/(\d{2})/', views.Myd.as_view(),name='cv'),
     url(r'^cv/(?P<n>\d{2})/', views.Myd.as_view(name='xxx'),name='cv'),
    #如果想给类的name属性赋值,前提你的Myd类里面必须有name属性(类属性,
    定义init方法来接受属性行不通,但是可以自行研究一下,看看如何行通,意义不大),
    并且之前类里面的name属性的值会被覆盖掉
    
    
    #view.py 中的写法
      class Myd(View):
        name = 'xxx'
    
        def get(self,request,n):
          print('get方法执行了')
          print('>>>',n)
          return render(request,'cvpost.html',{'name':self.name})
    
        def post(self,request,n):
          print('post方法被执行了')
          return HttpResponse('post')
    
     
    View Code

     

    ③添加类属性

      1) 第一种是常见的python的方法

    from django.http import HttpResponse
    from django.views import View
      
    class GreetingView(View):
        name = "yuan"
        def get(self, request):
             return HttpResponse(self.name)
      
    # You can override that in a subclass
      
    class MorningGreetingView(GreetingView):
        name= "alex"
    View Code

    2)第二种是 在url中设置的属性python

     

    urlpatterns = [
       url(r'^index/$', GreetingView.as_view(name="egon")), 
    #类里面必须有name属性,并且会被传进来的这个属性值给覆盖掉 ]

     

     

    七. 给视图加装饰器

      1.FBV使用装饰器

     

    def wrapper(func):
        def inner(*args, **kwargs):
            start_time = time.time()
            ret = func(*args, **kwargs)
            end_time = time.time()
            print("used:", end_time-start_time)
            return ret
        return inner
    
    
    # FBV版添加班级
    @wrapper
    def add_class(request):
        if request.method == "POST":
            class_name = request.POST.get("class_name")
            models.Classes.objects.create(name=class_name)
            return redirect("/class_list/")
        return render(request, "add_class.html")

     2.CBV使用装饰器

      ①

    from django.views import View
    from django.utils.decorators import method_decorator   #引入模块
     def wrapper(fn):   #装饰器框架
    
      Def inner(*args,**kwargs):
    
        print(‘Before’)
    
       ret=fn(*args,**kwargs)   # 函数前后可添加装饰
       Print(‘after’)
       Return ret
     Return inner
     
      # @method_decorator(wrapper,name=’get’)  指定请求方式添加装饰器
    
      class AddClass(View):
    
        @method_decorator(wrapper)   #  直接在开头添加装饰器
        def get(self, request):
            return render(request, "add_class.html")
    
        def post(self, request):
            class_name = request.POST.get("class_name")
            models.Classes.objects.create(name=class_name)
            return redirect("/class_list/")
    View Code

      ②

     

    
    
    from django.shortcuts import render,redirect,HttpResponse
    from django.urls import reverse
    from django.utils.decorators import method_decorator
    from django.views import View
    def wrapper(fn):
        def inner(request,*args,**kwargs):
            print('xxxxx')
            ret = fn(request)
            print('xsssss')
            return ret
        return inner
    
    # @method_decorator(wrapper,name='get')#CBV版装饰器方式一
    class BookList(View):
        @method_decorator(wrapper) #CBV版装饰器方式二
        def dispatch(self, request, *args, **kwargs):
            print('请求内容处理开始')
            res = super().dispatch(request, *args, **kwargs)  #进行 get 或 post 分配
            print('处理结束')
            return res
        def get(self,request):
            print('get内容')
            # all_books = models.Book.objects.all()
            return render(request,'login.html')
        @method_decorator(wrapper) #CBV版装饰器方式三
        def post(self,request):
            print('post内容')
            return redirect(reverse('book_list'))
    # @wrapper
    def book_list(request):
        
        return HttpResponse('aaa')
    View Code
    
    

    八.组件 (常用于导航条)

    {% include 'navbar.html' %}      在需要的地方按 此语法输入

     

      类似于模板的组件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            .c1{
                background-color: red;
                height: 40px;
            }
        </style>
    </head>
    <body>
    
    <div class="c1">
        <div>
            <a href="">xx</a>
            <a href="">dd</a>
        </div>
    </div>
    
    </body>
    </html>
    View Code

     需要组件的文件

    
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% include 'nav.html' %}
    <h1>xxxxxxxxxx</h1>
    </body>
    </html>
    
    

     

     

    转载于:https://www.cnblogs.com/lw1095950124/p/10447514.html

    展开全文
  • vue.js的官方介绍里可能提到过nuxt.js,我也不太清楚我怎么找到这个的 最近项目vue.js是主流了,当有些优化需求过来后,vue还是有点力不从心, 比如SEO的优化,由于vue在初始化完成之前,页面是没有任何内容的,...

    vue.js的官方介绍里可能提到过nuxt.js,我也不太清楚我怎么找到这个的

    最近项目vue.js是主流了,当有些优化需求过来后,vue还是有点力不从心,

    比如SEO的优化,由于vue在初始化完成之前,页面是没有任何内容的,所以基本上没有办法满足这个需求

    比如有些访问量较大的主页里面,由于都是异步数据,所以在服务器数据没有返回之前,可能只能无奈的显示一个loadding....但是产品没办法理解这里面的技术流,他们只知道,能不能更快一点,甚至不用loadding~

    我在vue这个环境下能做的:

    把vue公共的库打包为一个js扔cdn上,加快访问速度的同时,全局访问一次后,客户端webview可以从本地缓存直接加载

    简化热点数据请求次数,尽量在一次请求能完成的情况下一次请求完成

    路由懒加载,基本每个页都是一个单独的chunk

    可是在移动客户端产品来看,只是加快了一点加载的速度,体验上还是没什么改善。

    静态化可能是一种好的选择,

    这里可能不是介绍nuxt的用法,官方中文介绍文档已经很详细了:https://zh.nuxtjs.org/

    涉及的功能关键字:vue-cli, nuxt-express,nginx

    我的环境还是windows,所以这里用的nginx for windows(很多人说不行,我只是试一试,何必当真)

    下载一个压缩包,解压就可以用了,目录结构(版本:1.13.3)

     

     注意:解压的文件路径,不能用中文,否则服务无法启动

    安装vue-cli

     

    npm i vue-cli -D

     

    下载模板项目

    vue init nuxt/express sexpress

    初始化加载包

    npm install

    编译

    npm run build

    到这里模板项目可以尝试运行一下了

    npm run start

    浏览器http://localhost:3000可以看到结果了

    当我们把cmd的窗口关掉后,这个服务就自动停止了,如果需要一个常驻的服务去启动站点,这里需要安装一个pm2

    npm install pm2 -g

    安装成功后,pm2启动

    -----------------------------------分割线-----------------------------------------

    2018年12月10日更新

    这篇文章写得很早,当时还是nuxt的1.0的版本,最近有不少人QQ直接问,所以在此做相应的修改,

    现在的PM2启动命令更改如下

     pm2 start ./node_modules/nuxt/bin/nuxt 

    当然,你也可以去这个目录下面看看这个文件的内容,自己可以写一个简单的启动入口文件...

    -----------------------------------分割线-----------------------------------------

    修改本地host文件,站点名称自己定义

    127.0.0.1 www.mywebsite1.com

    修改nginx配置文件,/conf/nginx.conf,红字部分为修改的地方

    复制代码

    #user  nobody;
    worker_processes  1;
    
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    #error_log  logs/error.log  info;
    
    #pid        logs/nginx.pid;
    
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
        #                  '$status $body_bytes_sent "$http_referer" '
        #                  '"$http_user_agent" "$http_x_forwarded_for"';
    
        #access_log  logs/access.log  main;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
    
        upstream nuxtserver1 {
                server 127.0.0.1:3000;
        }
    
        server {
            listen       8088;
            server_name  www.mywebsite1.com;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location /{
                proxy_pass   http://nuxtserver1;
                index  index.html index.htm;
            }
    
            #error_page  404              /404.html;
    
            # redirect server error pages to the static page /50x.html
            #
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
    
            # proxy the PHP scripts to Apache listening on 127.0.0.1:80
            #
            #location ~ \.php$ {
            #    proxy_pass   http://127.0.0.1;
            #}
    
            # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
            #
            #location ~ \.php$ {
            #    root           html;
            #    fastcgi_pass   127.0.0.1:9000;
            #    fastcgi_index  index.php;
            #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            #    include        fastcgi_params;
            #}
    
            # deny access to .htaccess files, if Apache's document root
            # concurs with nginx's one
            #
            #location ~ /\.ht {
            #    deny  all;
            #}
        }
    
    
        # another virtual host using mix of IP-, name-, and port-based configuration
        #
        #server {
        #    listen       8000;
        #    listen       somename:8080;
        #    server_name  somename  alias  another.alias;
    
        #    location / {
        #        root   html;
        #        index  index.html index.htm;
        #    }
        #}
    
    
        # HTTPS server
        #
        #server {
        #    listen       443 ssl;
        #    server_name  localhost;
    
        #    ssl_certificate      cert.pem;
        #    ssl_certificate_key  cert.key;
    
        #    ssl_session_cache    shared:SSL:1m;
        #    ssl_session_timeout  5m;
    
        #    ssl_ciphers  HIGH:!aNULL:!MD5;
        #    ssl_prefer_server_ciphers  on;
    
        #    location / {
        #        root   html;
        #        index  index.html index.htm;
        #    }
        #}
    
    }

    复制代码

    nginx重新启动

    nginx -s reload

    nginx的日志文件在/logs下面,如果没有启动,可以看看error.log

    成功启动后,浏览器输入http://www.mywebsite1.com:8088/

     

     

    展开全文
  • url反向解析 在根urls中,使用 namespace 进行标记 在应用的urls中,使用name进行标记 查找的时候根据 namespace:name 带参数 位置参数 模板中写 {% url ‘namespace:name’ xxx yyy zzz %} ...
  • Redner的主要用途之一是通过梯度下降进行反向渲染(因此称为Redner)。 令红色更与众不同的是:1)通过适当考虑不连续性,它随机地计算正确的渲染梯度,而无需任何近似; 2)具有基于物理的模式-这意味着它可以模拟...
  • 输入下面的地址就可以得到结果了,这是因为写死了这个页面,因此,我们可以在视图这里也进行反向解析  起别名:   运行:(跳转之后打印的,浏览器现在的结果无所谓) 服务端打印的结果,转换成这个路径了.也...
  • 2-6模板渲染  好多页面都是一样的,我们怎么处理相同的东西??? 写商城,首页index,订单页,个人中心 base.html 也就是母版页 <! DOCTYPE html > < html lang ="zh-CN" > < head > < meta ...
  • Django因其丰富的组件、高度的稳定性、强大的后台管理,在Python Web开发上占有绝大的市场份额与地位,2小时上手Django框架、帮助学员最快速的理解Web的运行原理和Django环境的快速搭建,并通过Django的Admin,一键...
  • nginx反向代理

    2019-02-16 20:00:54
    同时比较擅长直接处理纯静态页面,纯静态页面不必通过django渲染出来。 正向和反向代理 正向场景:爬虫时的代理ip 反向场景:nginx ngrok 参考 https://blog.csdn.net/u011456940/article/details/53633991 nginx 安...
  • CNN的反向传播

    2018-02-21 19:03:00
    本博文具有大量公式,由于简书不支持公式,公式渲染完全版请移步博客 DNN中的反向传播 反向传播算法是神经网络的训练的基本算法组成之一,在训练神经网络时,训练分为两个步骤:计算梯度和更新权值。其中反向传播...
  • 什么是服务端渲染? 核心在于方便seo优化 后端先调用数据库,获得数据之后,将数据和页面元素进行拼装,组合成完整的html页面,再直接返回给浏览器,以便用户浏览。 例如:http://www.cnblogs.com/cate/design 什么...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 288
精华内容 115
关键字:

反向渲染