zoukankan      html  css  js  c++  java
  • Processing中PImage类和loadImage()、createImage()函数的相关解析

    聊一聊Processing中PImage类和loadImage()、createImage()函数。因为要借P5做多媒体创意展示,图片是一个很重要的媒体。有必要就图片的获取和展放作总结。

    首先

    有一点需要先提出来,PGraphics是继承自PImage的,看源码:

    public class PGraphics extends PImage implements PConstants {
    	...
    }
    

    因此,理论上所有的绘制函数其实它的绘制对象都是PImage,都在这张图纸上呈现内容,即默认的PGraphics g,可参考笔者另一篇:
    https://www.cnblogs.com/sharpeye/p/13734132.html
    这就给我们一个参考,意思是PGraphicsPImage时常要考虑两者互相转换的问题。实际运行时也是如此。

    其次

    PImage类中设有混合叠加的方法、图片文件的IO方法,即保存读取方法等,比较常用的是loadPixels()save()filter()等,当然还有许多内部成员变量,比如format width height pixels等等。其中format指的是颜色通道格式,比如有RGB ARGB ALPHA等。save()是可以保存带有alpha通道的图像的。
    PImage类要使用必须要new实例对象,一般的,无外乎是使用loadImage()createImage()这两个函数来获得这一对象。如果要读取一张现有的图像信息,那么就load
    根据官网说明,loadImage()有两参数可供填写,即:loadImage(filename, extension)
    filename指的是本地文件路径或者url文件路径。本地文件路径可以是绝对地址也可以是相对地址。
    extension指的是指定文件后缀,即文件类型匹配。如果于真实文件类型不匹配也能读进内存不过通道数据未必能读取,也就是alpha层的问题。文件类型共有几类 ".tga", ".jpg", ".png", ".jpeg", ".gif"等,".tif"无法读取,P5自己的.tiff保存文件是可以的。请见下文例子:

    PImage img1;
    PImage img2;
    PImage webImg;
    void setup() {
      size(500,500);
      img1 = loadImage("mypic.png");	//读取相对路径下的文件,即pde根目录下的文件,如果有data文件夹,则在此文件夹下寻找
      img2 = loadImage("d://mypic.png");//读取绝对路径下的文件
    
      String url = "https://processing.org/img/processing-web.png";//读取web互联网上的文件,也可读取局域网下的文件
      webImg = loadImage(url, "png");//选定读取的文件类型为png
    }
    
    void draw() {
      background(0);
    
      image(img1, 0, 0);
      image(img2, 200, 0);
      image(webImg, 0, 200);
    }
    

    会注意到,读取本地的文件速度非常理想,但是互联网上的文件会根据网络情况产生不少等待时间,给它看成是Processing的假死状态,这是不希望看到的情况,如何来避免呢?有个函数官方给我们了----requstImage()
    这个函数就可以避免假死状态,或者称之为非阻塞式读取,而传统读取是阻塞式的。实质上查阅源码会看到它新建了一个thread线程,因此,可以避免占用主线程而耽误往后的语句执行任务。代码如下:

    
      /**
       * ( begin auto-generated from requestImage.xml )
       *
       * This function load images on a separate thread so that your sketch does
       * not freeze while images load during <b>setup()</b>. While the image is
       * loading, its width and height will be 0. If an error occurs while
       * loading the image, its width and height will be set to -1. You'll know
       * when the image has loaded properly because its width and height will be
       * greater than 0. Asynchronous image loading (particularly when
       * downloading from a server) can dramatically improve performance.<br />
       * <br/> <b>extension</b> parameter is used to determine the image type in
       * cases where the image filename does not end with a proper extension.
       * Specify the extension as the second parameter to <b>requestImage()</b>.
       *
       * ( end auto-generated )
       *
       * @webref image:loading_displaying
       * @param filename name of the file to load, can be .gif, .jpg, .tga, or a handful of other image types depending on your platform
       * @param extension the type of image to load, for example "png", "gif", "jpg"
       * @see PImage
       * @see PApplet#loadImage(String, String)
       */
      public PImage requestImage(String filename, String extension) {
        // Make sure saving to this file completes before trying to load it
        // Has to be called on main thread, because P2D and P3D need GL functions
        if (g != null) {
          g.awaitAsyncSaveCompletion(filename);
        }
        PImage vessel = createImage(0, 0, ARGB);
    
        // if the image loading thread pool hasn't been created, create it
        if (requestImagePool == null) {
          ThreadFactory factory = new ThreadFactory() {
            public Thread newThread(Runnable r) {
              return new Thread(r, REQUEST_IMAGE_THREAD_PREFIX);
            }
          };
          requestImagePool = Executors.newFixedThreadPool(4, factory);
        }
        requestImagePool.execute(() -> {
          PImage actual = loadImage(filename, extension);
    
          // An error message should have already printed
          if (actual == null) {
            vessel.width = -1;
            vessel.height = -1;
    
          } else {
            vessel.width = actual.width;
            vessel.height = actual.height;
            vessel.format = actual.format;
            vessel.pixels = actual.pixels;
    
            vessel.pixelWidth = actual.width;
            vessel.pixelHeight = actual.height;
            vessel.pixelDensity = 1;
          }
        });
        return vessel;
      }
    

    能发现官方设立了一个线程池接口类ExecutorService,而后新建线程池,每次读取图片都新建一个线程入线程池,统一管理,并用ExecutorService实例的对象来execute执行,从而独立于主线程来执行任务。

    再次

    光有读取还不完善,来看看自己生成图片对象createImage()。很清楚,三个参数

    Parameters	
    w	int: width in pixels	图片对象宽度尺寸
    h	int: height in pixels	图片对象高度尺寸
    format	int: Either RGB, ARGB, ALPHA (grayscale alpha channel)	图片类型
    

    官方介绍到,要注意使用规范性,如下:

    PImage image = createImage(600,400,RGB);	//正确的实例PImage写法	其中图片通道类型有RGB, ARGB, ALPHA
    // *错误* PImage image = new PImage(600,400,RGB);		//错误的写法!!
    

    实际上参照源码,就是new PImage的方法实例:

    public PImage createImage(int w, int h, int format) {
      PImage image = new PImage(w, h, format);
      image.parent = this;  // make save() work
      return image;
    }
    

    值得思考的是:
    一、 PImage对象改变其像素无外乎要修改其pixels数组值,而不能再图片上直接绘制图形,如果要绘制则必须转换成PGraphics;
    二、 如果要复制PImage以实例多个对象,可以使用copy()或者clone(),比如:

    PImage op;
    PImage op2;
    
    try  {
        op = (PImage)img2.clone();	使用clone方法 注意转换类型
      }
      catch(Exception e) {
      }
      
    op2 = img2.copy():	使用copy方法
    

    三、 绘制在PGraphics上的方法有三种,一种是image(),一种是set(),还有一种是手动以遍历像素的形式来绘制渲染。如下:

      image(img1, 100, 0);
      image(img2, 200, 0);
      image(webImg, 0, 200);   //使用image方法
      
      set(0,0,img2);   //使用set方法
    
      loadPixels();
      img1.loadPixels();
      for (int x = 0; x < img1.width; x++) {
        for (int y = 0; y < img1.height; y++) {
          color c = img1.pixels[y*img1.width + x];
          if(alpha(c) == 0)   continue;   //alpha通道检测
          pixels[y*width + x] = c ;   //相当于 g.pixels[]...   使用了pixels数组直接赋值
        }
      }
      updatePixels();
    
    //************************************//   另一种手动填充像素 set
      img1.loadPixels();
      for (int x = 0; x < img1.width; x++) {
        for (int y = 0; y < img1.height; y++) {
          color c = img1.pixels[y*img1.width + x];
          //if(alpha(c) == 0)   continue;  使用set方法  alpha通道检测可跳过
          set(x,y,c);   //使用set方法    注意使用set方法跟updatePixels方法有冲突,这里去掉其调用
        }
      }
    //************************************//
    
    

    最后

    当然,在实际使用中,会用到大量技巧,比如image()set()两函数渲染图片效率有出入,一般可以用set()来提高渲染效率,但是无法进行矩阵变换等运算。再如,PImage中的save()可以保存带通道的图片。这次就简单做一总结,我们往后再细聊,感谢阅读!!

  • 相关阅读:
    Android SDK在线更新镜像服务器
    redis
    自动
    Java编程时部分快捷键
    问题解决路
    35
    【JavaScript 8—基础知识点】:DOM
    【JavaScript 7—基础知识点】:BOM
    【JavaScript 6—基础知识点】:正则表达式(应用)
    【JavaScript 5—基础知识点】:正则表达式(笔记)
  • 原文地址:https://www.cnblogs.com/sharpeye/p/14954114.html
Copyright © 2011-2022 走看看