Soooo many problems... (images, masking, apde)

MadMad
edited September 2017 in Android Mode

So. New forum. First post. Frustrated.

I don't claim to write clean code, but I try to make it readable. I started programming somewhere in the 80s, in BASIC, then in C, C++, whatever. I love global variables, I can do Object Oriented. I was looking for a way to program in Java directly on Android, with the result being an APK. So I came to Processing. Hm. I would have made APDE as some kind of dual-mode tool chain, where one mode is the basic "Go and write processing" and the other mode as "programm natively, use P as import, do your own main without overriding", but that's just me and outside the scope of what I'm trying to do here.

First I went ahead and grabbed me android's sensors just to see if I could. I made my own sensor class, grabbed temperature from the battery manager, made another class (batman), registered some callbacks, put them on screen, added a background. Worked.

I started grabbing touch input as mouse input, worked just fine. Wanted to text() on a PGraphics to help debug. Easy-peasy. Make a PGraphics, text in, display it by image(..), clear() it, text in, display, clear, rinse, repeat. Huh. What's that? the text overwrites and overwrites and overwrites (I was grabbing touch position, fingerdown, fingerup, worked like a charm.) but wasn't clearing. I wanted to re-use the PGraphics with clear() because creating a new one 30x per second is not the way to go, right? But no, clear() does nothing but put a new "backround" on top, full black, fully transparent. On top. Yeah, you read that right. Took me 4 hours of my life and diving into Processing on github. (censored). By the way, whenever someone on github or here found some problem with clear() and pgraphics, the comments are a hoot. Yeah, I can reproduce it. No, I will not loop over 300,000 pixels to reset them 30 times a second, thanks for recommending. Even the clear() example right in the procesing reference (modified to draw on touch, clear on finger-up) does not work on Android. Let me repeat that: IT DOES NOT WORK. Want a (censored) video?

Next, with a lightsensor running, I wanted to build some photographer's helper. Some overlaying rotating discs, aperture, shutter speed, a draggable ISO selector... Let's start with a disc. Made me some nice square metal plate, read some more. Either I take it and mask it with something round to get a disc, or I apply it as a texture onto a circle. Since my first hour trying to texture(...) resp. setTexture(...) somehow failed to bring results, and I saw that masking was the obviously preferred way to go in the forum, I went down that rabbithole.

Screenshot_2017-09-02-17-10-38

Want some code? You get some code.

import android.os.Bundle;
import android.os.Build;
import android.app.Activity;
import android.content.Context;
import android.view.WindowManager;
import android.view.Display;

Activity activity;
Context context;
Display display;

int rotation;
String rotString;
String orientation;
float aspectRatio;

int canvasX;
int canvasY;
int fontSizeNormal = 5;
int colWidth;
int rowHeight;
int rows;
int cols;
int gridUnitX;
int gridUnitY;

float fScale;
int iScale;

int i;
int loopcount = 0;

PGraphics backgroundG;
PGraphics maskG;
PGraphics balouG;
PGraphics balouMaskedG;
PGraphics testG;
PGraphics jerkG;

PImage bgIMG;
PImage testIMG;
PImage balouIMG;
PImage balouorigIMG;
PImage cJerkIMG;

public void setup() {
  //fullScreen();

  //orientation(PORTRAIT);

  size(displayWidth, displayHeight, P2D);
  frameRate(30);
  
  background(0,0,0);
  
  canvasX = displayWidth;
  canvasY = displayHeight;
  activity = this.getActivity();
  context = activity.getApplicationContext();

  display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); 
  
  fontSizeNormal = int(float(canvasX)/18);
  rows = 2; cols = 3;
  gridUnitX = int(float(canvasX) / 11);
  gridUnitY = int(float(canvasY) / 14);

  fScale = canvasX / 1000;
  iScale = int(fScale);
  
  backgroundG = createGraphics(canvasX, canvasY);
  bgIMG = loadImage("background.jpg");
  while (bgIMG.width == 0) { delay(100); }
  backgroundG.beginDraw();
  backgroundG.image(bgIMG,0,0,canvasX, canvasY);  // fit to canvas on-the-fly
  backgroundG.endDraw();

  image(backgroundG,0,0);  // let's get this out of the way


  balouG = createGraphics(300, 300);
  balouorigIMG = loadImage("balou.jpg");
  while (balouorigIMG.width == 0) { delay(100); }
  balouorigIMG.resize(300,300);  // resize it
  balouG.beginDraw();
  balouG.image(balouorigIMG,0,0);  // and put it in the upper left corner of PGraphics balouG
  balouG.endDraw();

  maskG = createGraphics(300,300);
  maskG.beginDraw();
  maskG.ellipse(150, 150, 300, 300);  // nice white circle
  maskG.endDraw();

  balouMaskedG = createGraphics(300, 300);
  balouIMG = loadImage("balou.jpg");
  while (balouIMG.width == 0) { delay(100); }
  balouIMG.resize(300,300);           // if I don't resize here, 
  balouIMG.mask(maskG);               // I get an error that the mask doesn't fit the image
  balouMaskedG.beginDraw();           // so something happens in resize(int, int)
  balouMaskedG.image(balouIMG,0,0);   // but what?!?
  balouMaskedG.endDraw();

  // and just for the heck of it:
  balouMaskedG.clear();       // outside a beginDraw/endDraw
  balouMaskedG.beginDraw();
  balouMaskedG.clear();       // inside a beginDraw/endDraw
  balouMaskedG.endDraw();
  // at least one of those should return balouMaskedG to a completely transparent state.
  // if you check the Processing -> Android port's source, you should immediately see why that fails.

  testG = createGraphics(300, 300);
  testIMG = loadImage("balou.jpg");
  while (testIMG.width == 0) { delay(100); }
  testG.beginDraw();
  testG.image(testIMG,0,0,300,300);  // fit to 300x300 canvas
  testG.endDraw();

  // Afterthought: 
  testIMG.resize(300,300);   // should work just fine, right?

  jerkG = createGraphics(300, 300);
  cJerkIMG = loadImage("balou.jpg");
  while (cJerkIMG.width == 0) { delay(100); }
  cJerkIMG.resize(300,300);  // resize it
  jerkG.beginDraw();
  jerkG.image(cJerkIMG,0,0);  // and put it in the upper left corner of PGraphics
  cJerkIMG.mask(maskG);
  jerkG.image(cJerkIMG,50,50);  // and NOT put it in the upper left corner of PGraphics
  jerkG.clear(); // does nothing
  jerkG.endDraw();
  jerkG.clear(); // still does nothing

  loop();
}


public void draw() {
  textSize(15);
  fill(200,200,200);

  background(0);          // black is beautiful

  image(backgroundG,0,0);  // nicely stretched background
  text("Background fit to canvas on-the-fly",0,20);

  image(maskG,25,25);   // white circle
  text("Mask",25,50);

  image(balouG,350,25); // nicely resized PGraphics
  text("resized and\nput into PGraphics",350,50);

  image(testG,675,25);  // nicely resized PGraphics
  text("on-the-fly-resize while\nput into PGraphics",50,375);

  image(balouMaskedG,1000,25); //gives a black circle?!?
  text("resized, masked and\nput into PGraphics\n(should be cleared)",1000,50);

  image(balouorigIMG,25, 350);
  text("resized image\nWITHOUT PGraphics\nbut put through a PGraphics\nbefore",25,375);

  image(testIMG,350,350);  // 
  text("just a resized image\nwithout PGraphics",350,375);
  
  image(cJerkIMG,675,350); // 
  text("Why is that black\noutside the cirle??",675,375);

  image(jerkG,1000,350); // 
  text("I go destroy something now.",1000,375);

} // last line of draw()

Sorry for those seemingly random missing linebreaks, we have some translation error here(LF/CRLF stuff, old but gold). Also, there are a lot of unused vars, as I yanked that out of the lightmeter sketch and expanded on it.

Anyways. Now, if I resize a loaded image, it'll be black unless I draw it into a PGraphics? Really? And the mask does nothing unless I put the masked image through a PGraphics, and then the background is black? Why? I feel like Del Shannon crying Why why why why why in"Runaway".

And don't even get me started on lines in the console, like "The pixel array has a length of 90000, but it should be at least 184041" So, who made that pixel array too small?

Flame me, boot me, whatever, but this needed out, I already had one heart attack, I don't need another one. You need one non-300x300px image to test that code yourself.

Answers

  • @Mad===

    try to format your code, it will be more easy to use it!

  • As I said: Sorry for those seemingly random missing linebreaks, we have some translation error here(LF/CRLF stuff, old but gold).

  • nothing changed, just formatted

    import android.os.Bundle; 
    import android.os.Build; 
    import android.app.Activity; 
    import android.content.Context; 
    import android.view.WindowManager; 
    import android.view.Display;
    
    Activity activity; 
    Context context; 
    Display display;
    
    int rotation; 
    String rotString; 
    String orientation; 
    float aspectRatio;
    
    int canvasX; 
    int canvasY; 
    int fontSizeNormal = 5; 
    int colWidth; 
    int rowHeight; 
    int rows; 
    int cols; 
    int gridUnitX; 
    int gridUnitY;
    
    float fScale; 
    int iScale;
    
    int i; 
    int loopcount = 0;
    
    PGraphics backgroundG; 
    PGraphics maskG; 
    PGraphics balouG; 
    PGraphics balouMaskedG; 
    PGraphics testG; 
    PGraphics jerkG;
    
    PImage bgIMG; 
    PImage testIMG; 
    PImage balouIMG; 
    PImage balouorigIMG; 
    PImage cJerkIMG;
    
    public void setup() { 
      //fullScreen();
    
      //orientation(PORTRAIT);
    
      size(displayWidth, displayHeight, P2D); 
      frameRate(30);
    
      background(0, 0, 0);
    
      canvasX = displayWidth; 
      canvasY = displayHeight; 
      activity = this.getActivity();  
      context = activity.getApplicationContext();
    
      display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    
      fontSizeNormal = int(float(canvasX)/18); 
      rows = 2; 
      cols = 3; 
      gridUnitX = int(float(canvasX) / 11); 
      gridUnitY = int(float(canvasY) / 14);
    
      fScale = canvasX / 1000; 
      iScale = int(fScale);
    
      backgroundG = createGraphics(canvasX, canvasY); 
      bgIMG = loadImage("background.jpg"); 
      while (bgIMG.width == 0) { 
        delay(100);
      } 
      backgroundG.beginDraw(); 
      backgroundG.image(bgIMG, 0, 0, canvasX, canvasY); // fit to canvas on-the-fly 
      backgroundG.endDraw();
    
      image(backgroundG, 0, 0); // let's get this out of the way
    
      balouG = createGraphics(300, 300); 
      balouorigIMG = loadImage("balou.jpg"); 
      while (balouorigIMG.width == 0) { 
        delay(100);
      } 
      balouorigIMG.resize(300, 300); // resize it
      balouG.beginDraw(); 
      balouG.image(balouorigIMG, 0, 0); // and put it in the upper left corner of PGraphics balouG
      balouG.endDraw();
    
      maskG = createGraphics(300, 300);   
      maskG.beginDraw();   
      maskG.ellipse(150, 150, 300, 300); // nice white circle  
      maskG.endDraw();
    
      balouMaskedG = createGraphics(300, 300); 
      balouIMG = loadImage("balou.jpg"); 
      while (balouIMG.width == 0) { 
        delay(100);
      } 
      balouIMG.resize(300, 300); // if I don't resize here,  
      balouIMG.mask(maskG); // I get an error that the mask doesn't fit the image 
      balouMaskedG.beginDraw(); // so something happens in resize(int, int) 
      balouMaskedG.image(balouIMG, 0, 0); // but what?!? 
      balouMaskedG.endDraw();
    
      // and just for the heck of it:
      balouMaskedG.clear(); // outside a beginDraw/endDraw 
      balouMaskedG.beginDraw(); 
      balouMaskedG.clear(); // inside a beginDraw/endDraw 
      balouMaskedG.endDraw(); // at least one of those should return balouMaskedG to a completely transparent state.
      // if you check the Processing -> Android port's source, you should immediately see why that fails.
    
      testG = createGraphics(300, 300); 
      testIMG = loadImage("balou.jpg"); 
      while (testIMG.width == 0) { 
        delay(100);
      } 
      testG.beginDraw(); 
      testG.image(testIMG, 0, 0, 300, 300); // fit to 300x300 canvas 
      testG.endDraw();
    
      // Afterthought:
      testIMG.resize(300, 300); // should work just fine, right?
    
      jerkG = createGraphics(300, 300); 
      cJerkIMG = loadImage("balou.jpg"); 
      while (cJerkIMG.width == 0) { 
        delay(100);
      } 
      cJerkIMG.resize(300, 300); // resize it
      jerkG.beginDraw(); 
      jerkG.image(cJerkIMG, 0, 0); // and put it in the upper left corner of PGraphics
      cJerkIMG.mask(maskG); 
      jerkG.image(cJerkIMG, 50, 50); // and NOT put it in the upper left corner of PGraphics
      jerkG.clear(); // does nothing 
      jerkG.endDraw(); 
      jerkG.clear(); // still does nothing
    
      loop();
    }
    
    public void draw() { 
      textSize(15); 
      fill(200, 200, 200);
    
      background(0); // black is beautiful
    
      image(backgroundG, 0, 0); // nicely stretched background 
      text("Background fit to canvas on-the-fly", 0, 20);
    
      image(maskG, 25, 25); // white circle
      text("Mask", 25, 50);
    
      image(balouG, 350, 25); // nicely resized PGraphics 
      text("resized and\nput into PGraphics", 350, 50);
    
      image(testG, 675, 25); // nicely resized PGraphics
      text("on-the-fly-resize while\nput into PGraphics", 50, 375);
    
      image(balouMaskedG, 1000, 25); //gives a black circle?!? 
      text("resized, masked and\nput into PGraphics\n(should be cleared)", 1000, 50);
    
      image(balouorigIMG, 25, 350); 
      text("resized image\nWITHOUT PGraphics\nbut put through a PGraphics\nbefore", 25, 375);
    
      image(testIMG, 350, 350); //
      text("just a resized image\nwithout PGraphics", 350, 375);
    
      image(cJerkIMG, 675, 350); //
      text("Why is that black\noutside the cirle??", 675, 375);
    
      image(jerkG, 1000, 350); // 
      text("I go destroy something now.", 1000, 375);
    } // last line of draw() 
    
  • MadMad
    Answer ✓

    Thanks, I just found that PRE works here. Sorry again.

  • What sorcery is this? Now this thread is marked as answered?!?

  •   while (balouIMG.width == 0) { 
        delay(100);
      } 
    

    you don't need these. loadImage blocks until it is done.

  • testIMG = loadImage("balou.jpg");
    

    and you only need to load the image once, you can copy it the other times. (using the copy() method on PImage)

  • Thanks, I just found that PRE works here

    until it doesn't. the official way is to highlight the code and press ctrl-o.

    New forum

    read the Common Questions over there on the left.

  • @koogs I had requestImage(...) in there for a while. Just left the while(...) in, might change back. Copy() works unless you run in some ArrayOutOfBounds, or get some more black squares. Erring on the safe side meant distrusting copy in this scenario, for me.

    Now, is there anyone with some idea why (all that PGraphics stuff) or why transparent isn't? Otherwise I'd just have to do it all myself, and use APDE only as an editor/compiler/dexer/packer/signer toolchain. Oh, wait, scratch that editor part, because.... ah well, don't even get me started on that one. But thanks for the toolchain.

  • ok, long rambling post which mentions several problems.

    you might get further posting ONE error at a time. with a minimal runnable code and a clear description of your expectations (mock-up expected result images if that helps). rather than the 8 vague problems we currently have.

    (extra points for modular code rather than one monolithic block

    void setup() {
      ...
      backgroundG = createBackgroundG();
      maskG = createMaskG();
      // etc
    }
    

    simple well-defined methods, solvable individually. eating an elephant one bit at a time.)

  • @Mad

    You need to remember Processing is and open source project. Therefore, any errors and improvements are done by the group of people that are under the Processing foundation and by volunteers like most people in the forum. In other words, there is no guarantee everything works in Processing as advertised. As new versions are released, new bugs can be generated and they need to be addressed. The best way (read this more as "proper way" although no guarantee any fast turn out response) to address bugs in Processing is by creating a github ticket describing your problem. What you could do is to create a ticket, describe your problem and provide an MVCE. You can add previous post relevant to your discussion to reinforced your point. If you create a post in the forum, also added to your ticket so any generated discussions are visible from the ticket in github. My understanding is that the Processing foundation will address the ticket based on priority, meaning how bad the bug is.

    Even the clear() example right in the procesing reference (modified to draw on touch, clear on finger-up) does not work on Android. Let me repeat that: IT DOES NOT WORK.

    Now, related to the clear() function... yes, I have experience that problem myself. People make assumptions that what works for one flavor of Processing, it should work in another. That is incorrect. Processing Java, Processing Android, Processing P5.js, Processing.R, Processing.js, Processing.py are all projects that are based on the Processing java flavor. However, the mechanics under the hood are different and to try to support the functionalities across each flavor is not easy and really depends on how many people are directly contributing to the source code.

    I also agree with koogs. Simple post addressing each issue is the way to go here. I wouldn't know where to start in your post as I can see what you mean but I will need to track your code to link it to your comments. That just means it takes a bit more effort to do. If you create simple post addressing single questions, you will have more people looking into that.

    I wanted to re-use the PGraphics with clear() because creating a new one 30x per second is not the way to go, right?

    My contribution to clear(), yes... did you try creating a new PGrahpics buffer instead of resetting its pixels? If it works, well... problem solved. If, it doesn't... then tell us what it didn't work. Don't be afraid of abusing your device just to make a point. This could not be a good practice, but if it works then you can move to another problem. That is what most people have done in the forum and that is probly the reason why it hasn't been address in the first place.

    Kf

  • @kfrajer Thank you for taking the time to respond in that fashion. I understand that serving smaller bites makes it easier to solve the underlying riddles. I was trying to find out where exactly the riddle is, the above code is a result of that. In other words, there's an elephant, it is ill, I don't exactly know where, but I tried to collect symptoms.

    I fought hard with the mask problem and the transparency. Seems if (mask or image) is ARGB and the other is RGB, then mask() reacts strangely. Also, l'm not sure if loadImage returns ARGB or RGB when loading an image without transparency like JPG, didn't dig deeper there. Sadly, there is no param for that in loadImage(), but:

    PImage fineImage;
    int targetWidth, targetHeight;
    
    // setup { .... }  with createMyMaskedImage() and setting vars;
    
    // draw { .... } with image(fineImage, x, y);
    
    public void createMyMaskedImage() {
      imageMode(CORNER);
      PImage sourceImg = loadImage("metal.jpg");   // image of unknown size
      while (sourceImg.width == 0) { delay(100); }  // yeah, I left that in
      sourceImg.resize(targetWidth, targetHeight );   // don't even try to display that
    
      fineImage = createImage( targetWidth, targetHeight, RGB);  // !!
      fineImage = sourceImg.get();   // this you can display !
    
      PGraphics maskPG = createGraphics( targetWidth, targetHeight );
      maskPG.beginDraw();
      maskPG.ellipse( targetWidth/2, targetHeight/2, targetWidth, targetHeight ); //or whatever you need
      maskPG.endDraw(); 
    
      PImage maskPIimg = createImage( targetWidth, targetHeight ,RGB);
      maskPImg = maskPG.get();  // now mask is a nice PImage from a PGraphics
    
      fineImage.mask(maskPImg);   // this works just fine, draw with image(...)
    }
    

    Yes, I know, that's not runnable without some more declarations, but it should help.

    About abusing my hardware: you don't even want to know... Yes, I tried just creating a PGraphics for debug output by just making a new one each draw(). That's sloooooooow! Like, dividing framerate by 20 slow. Don't do that in draw(). Nononono. No.

    I wonder if.... 1px*1px transparent.gif once resized and somehow brutally loaded into a PGraphics might do the trick.... Back later, maybe.

  • MadMad
    edited September 2017

    About CLEAR()-ing a PGraphics: Runnable Proof-of-concept.

    int canvasX;
    int canvasY;
    
    PGraphics backgroundG;
    PGraphics testG;
    PGraphics clearG;
    
    PImage bgIMG;
    PImage clear;
    
    public void setup() {
      size(displayWidth, displayHeight, P2D);
      frameRate(30);
      background(0,0,0);
    
      canvasX = displayWidth;
      canvasY = displayHeight;
    
      backgroundG = createGraphics(canvasX, canvasY);
      bgIMG = loadImage("background.jpg");
      while (bgIMG.width == 0) { delay(100); }
      backgroundG.beginDraw();
      backgroundG.image(bgIMG,0,0,canvasX, canvasY);  // fit to canvas on-the-fly
      backgroundG.endDraw();
    
      PImage tpx = loadImage("clear.png");  //which is not totally clear, to see it cumulating instead of replacing
      tpx.resize(500,500);
      clear = tpx.get();
    
      testG = createGraphics(500, 500);
      clearG = createGraphics(500, 500);
      clearG.beginDraw();
      clearG.image(clear,0,0);
      clearG.endDraw();   // so clearG has only one layer of the resized png in it and is fully initialized
    
      loop();
    }
    
    
    public void draw() {
      textSize(15);
      fill(200,200,200);
    
      image(backgroundG,0,0);  // nicely pre-stretched background
      text("Background fit to canvas",0,20);
    
      testG = createGraphics(500, 500);   // has NO effect here
    
      testG.beginDraw();
      // testG.blendMode(REPLACE); // not available in default or P2D, throws warning at runtime
      // testG.loadImage() doesn't work, not a method of PGraphics - would have been nice
      testG = clearG;              // was hoping to just copy from a virgin PGraphics - doesn't even barf
      testG.background(clear);     // loads just on top
      testG.background(0,0,0,0);   // loads on top instead of replacing as per reference
      testG.flush();               // does nothing visible
      testG.clear();               // does nothing visible
      testG.loadPixels();
      for (int i=0; i < testG.pixels.length; i++)  {
        testG.pixels[i] = color(0,0,0,0);    // brute forcing it to transparent does work. SLOOOOOOOOOOOOOW.
      }
      testG.updatePixels();
      testG.textSize(144);
      testG.fill(200);
      testG.text(mouseX + "\n" + mouseY,50,150);
      testG.endDraw();
    
      image(testG, 100,100);
      image(clear, 100,100); 
    } // last line of draw()
    

    if you out-comment the for(){}, you see the slightly tinted clear image cumulating. You'll also feel the speed difference. You could put all the testG stuff in a function, and declare PGraphics there instead of globally, then image(testG...) from the function. Tried that. Hurts watching.

Sign In or Register to comment.