You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cocoon.apache.org by JeVeoy <jo...@wisloff.org> on 2011/05/19 15:49:21 UTC

Streaming buffered image using Cocoon

I'm using Cocoon 2.2

Case

I would like to merge 2 images in PNG format on the server (buffered image)
and then send the result back as a stream. Thus NOT saving the image on the
server.

As an example you could have a nice background picture of a field. The field
(background image) changes according to season (winter, spring etc). The
user has the option to choose what kind of tree to display on the field.
He/she can select from a wide range of fruit trees like olive, apple etc.

One option is to save all possible combinations as unique images on the
server. I want to avoid this since the total number of combinations could
get very high. It could get really difficult to maintain changes to
backgrounds and trees. I would prefer to have 4 background images and 1
image for every tree and combine these on the fly and present the result to
the user without saving it on the server.

Merge

The merge itself is ok and works like a charm. Code written in Java uses
java.awt.image.BufferedImage.

Code

    // base path of the images
    File path = new File("C:/images");

    // load source images
    BufferedImage image = ImageIO.read(new File(path,
"background_summer.png"));
    BufferedImage overlay = ImageIO.read(new File(path, "tree_apple.png"));
    
    // create the new image, canvas size is the max of both image sizes
    int w = Math.max(image.getWidth(), overlay.getWidth());
    int h = Math.max(image.getHeight(), overlay.getHeight());
    BufferedImage combined = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
    
    // paint both images, preserving the alpha channels
    Graphics g = combined.getGraphics();
    g.drawImage(image, 0, 0, null);
    g.drawImage(overlay, 0, 0, null);
    
    // Save as new image
    ImageIO.write(combined, "PNG", new File(path, "combined.png"));

Using PNG format on image enable result with transparency.

And here comes the challenge. Instead of saving image to disc and then
referring to it in sitemap.xmap I would like to return the stream.

As far as my understanding goes I would have to catch the request in
sitemap.xmap (map:match pattern="getTree.*") and then call flow script using
map:call function with map:parameter. In the flow-function I would get the
parameter (in this case, the type of tree to display) and call the
java-function as written in the above snippet.

But how do I return a stream as a PNG image to the browser using
sitemap.xmap?
-- 
View this message in context: http://old.nabble.com/Streaming-buffered-image-using-Cocoon-tp31655697p31655697.html
Sent from the Cocoon - Users mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org


Re: Streaming buffered image using Cocoon

Posted by JeVeoy <jo...@wisloff.org>.
You, Sir, are a genius!

To be honest: it's actually my birthday today and you just gave me the best
present!

Thank you so much, Thomas "it's simple" Markus :)

Best Regards,
JeVeoy



Thomas Markus wrote:
> 
> hi,
> 
> it remains pretty simple :)
> 
> regards
> Thomas
> 
> <map:match pattern="time.jpg">
> <map:call function="intercept" />
> </map:match>
> <map:match pattern="generate.image">
> <map:read type="timeimg"/>
> </map:match>
> 
> 
> function intercept() {
>      var backgroundVariable = '#44ffFF';
>      var text = String(new Date());
>      cocoon.sendPage("generate.image", {
>          "background" : backgroundVariable,
>          "text" : text,
>          "size" : 55.3
>      });
> }
> 
> public class TestImageReader extends AbstractReader {
> 
>      public String getMimeType() {
>          return "image/png";
>      }
> 
>      private Object get(String name, Scriptable s) {
>          Object o = s.get(name, s);
>          while (o instanceof Wrapper) {
>              o = ((Wrapper) o).unwrap();
>          }
>          return o;
>      }
> 
>      @Override
>      public void generate() throws IOException, SAXException, 
> ProcessingException {
>          Object contextObject = FlowHelper.getContextObject(objectModel);
>          Object text = null;
>          Object background = null;
>          float fontsize = 80.f;
> 
>          if (contextObject instanceof Scriptable) {
>              Scriptable s = (Scriptable) contextObject;
>              background = get("background", s);
>              text = get("text", s);
>              Object size = get("size", s);
>              if (size instanceof Number) {
>                  fontsize = ((Number) size).floatValue();
>              }
>          }
> 
>          if (text == null)
>              text = DateFormat.getDateTimeInstance().format(new Date());
>          if (background == null)
>              background = "#FF0000";
>          Font font = Font.decode(Font.SERIF).deriveFont(fontsize);
>          TextLayout textLayout = new TextLayout(String.valueOf(text), 
> font, new FontRenderContext(null, true, true));
>          Rectangle2D bounds = textLayout.getBounds();
>          BufferedImage image = new BufferedImage((int) 
> (bounds.getWidth() + 0.5), (int) (bounds.getHeight() + 0.5), 
> BufferedImage.TYPE_INT_ARGB);
>          Graphics2D g = image.createGraphics();
>          g.setColor(Color.decode(String.valueOf(background)));
>          textLayout.draw(g, (float) -bounds.getX(), (float)
> -bounds.getY());
>          g.dispose();
>          ImageIO.write(image, "png", this.out);
>      }
> 
> }
> 
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
> For additional commands, e-mail: users-help@cocoon.apache.org
> 
> 
> 

-- 
View this message in context: http://old.nabble.com/Streaming-buffered-image-using-Cocoon-tp31655697p31697410.html
Sent from the Cocoon - Users mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org


Re: Streaming buffered image using Cocoon

Posted by Thomas Markus <t....@proventis.net>.
hi,

it remains pretty simple :)

regards
Thomas

<map:match pattern="time.jpg">
<map:call function="intercept" />
</map:match>
<map:match pattern="generate.image">
<map:read type="timeimg"/>
</map:match>


function intercept() {
     var backgroundVariable = '#44ffFF';
     var text = String(new Date());
     cocoon.sendPage("generate.image", {
         "background" : backgroundVariable,
         "text" : text,
         "size" : 55.3
     });
}

public class TestImageReader extends AbstractReader {

     public String getMimeType() {
         return "image/png";
     }

     private Object get(String name, Scriptable s) {
         Object o = s.get(name, s);
         while (o instanceof Wrapper) {
             o = ((Wrapper) o).unwrap();
         }
         return o;
     }

     @Override
     public void generate() throws IOException, SAXException, 
ProcessingException {
         Object contextObject = FlowHelper.getContextObject(objectModel);
         Object text = null;
         Object background = null;
         float fontsize = 80.f;

         if (contextObject instanceof Scriptable) {
             Scriptable s = (Scriptable) contextObject;
             background = get("background", s);
             text = get("text", s);
             Object size = get("size", s);
             if (size instanceof Number) {
                 fontsize = ((Number) size).floatValue();
             }
         }

         if (text == null)
             text = DateFormat.getDateTimeInstance().format(new Date());
         if (background == null)
             background = "#FF0000";
         Font font = Font.decode(Font.SERIF).deriveFont(fontsize);
         TextLayout textLayout = new TextLayout(String.valueOf(text), 
font, new FontRenderContext(null, true, true));
         Rectangle2D bounds = textLayout.getBounds();
         BufferedImage image = new BufferedImage((int) 
(bounds.getWidth() + 0.5), (int) (bounds.getHeight() + 0.5), 
BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = image.createGraphics();
         g.setColor(Color.decode(String.valueOf(background)));
         textLayout.draw(g, (float) -bounds.getX(), (float) -bounds.getY());
         g.dispose();
         ImageIO.write(image, "png", this.out);
     }

}



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org


Re: Streaming buffered image using Cocoon

Posted by JeVeoy <jo...@wisloff.org>.
Ah...Beautiful! Thank you for providing a working sample! Awesome!

But what if I wanted to perform check against values in cocoon.session,
cocoon.context or perhaps business logic? What I'm thinking of is to
intercept the request in sitemap.xmap like this:

  <map:match pattern="time.jpg">
    <map:call function="intercept"/>
  </map:match>

Then I would have a flowscript function like this:

  function intercept() {
    // Session valid? Using cocoon.session to validate...
    ...
    // Fetch some DB values if session valid...
    var businessLogicObject = cocoon.getComponent("blBean");
    ...
    // Etc..
    
    // I know that user has a valid session, I've fetched values from
context (like language variables),
    // performed a fetch towards DB and now I want to send everything needed
to generate a png-image
    cocoon.sendPage("generate.image", {"background" : backgroundVariable,
"overlay" : overlayVariable});
  }

The idea was to make a sitemap entry like this:

  <map:match pattern="generate.image">
    <map:read type="timeimg"/>
  </map:match>

This utilizes the generator you wrote, but in this case I get an exception:
Pipeline has already been processed for this request. I've omitted
parameters, as you can see, for the time being.

Is there any simple solution to make use of a flowscript function to perform
logic and then use your reader with parameters?

Thomas Markus wrote:
> 
> Hi,
> 
> thats pretty simple. Create a reader like this (creates a png with 
> current timestamp in red):
> 
> public class TestImageReader extends AbstractReader {
> 
>      public String getMimeType() {
>          return "image/png";
>      }
> 
>      public void generate() throws IOException, SAXException, 
> ProcessingException {
>          String date = DateFormat.getDateTimeInstance().format(new
> Date());
>          Font font = Font.decode(Font.SERIF).deriveFont(80.f);
>          TextLayout textLayout = new TextLayout(date, font, new 
> FontRenderContext(null, true, true));
>          Rectangle2D bounds = textLayout.getBounds();
>          BufferedImage image = new BufferedImage((int) 
> bounds.getWidth(), (int) bounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
>          Graphics2D g = image.createGraphics();
>          g.setColor(Color.RED);
>          textLayout.draw(g, (float) -bounds.getX(), (float)
> -bounds.getY());
>          g.dispose();
>          ImageIO.write(image, "png", this.out);
>      }
> 
> }
> 
> in your sitemap define and use this reader:
> <map:components>
> <map:readers>
> <map:reader name="timeimg" src="test.TestImageReader" />
> </map:readers>
> </map:components>
> 
> ...
> 
> <map:match pattern="time.jpg">
>    <map:read type="timeimg" />
> </map:match>
> 
> 
> hth
> Thomas
> 
> 
> Am 21.05.2011 14:00, schrieb JeVeoy:
>>
>> First of all: thank you so much for helping me out, Steven&  Thomas!
>>
>> Then: I've tried my best to see if I could get a working example to run
>> using either javax.imageio.ImageIO.write(RenderedImage, String,
>> OutputStream) or AbstractReader.out, but to no luck. Googling didn't
>> quite
>> help either. I'm very sorry for my lack of knowledge... Hope you can
>> forgive
>> me.
>>
>> Pipeline for an image-reader is, as far as I know, defined in
>> sitemap.xmap
>> like this:
>>
>> <map:match pattern="*.jpg">
>>    <map:read type="image" src="path/{1}.png" mime-type="image/png">
>>    </map:read>
>> </map:match>
>>
>> How can I make use of AbstractReader.out in this definition?
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
> For additional commands, e-mail: users-help@cocoon.apache.org
> 
> 
> 

-- 
View this message in context: http://old.nabble.com/Streaming-buffered-image-using-Cocoon-tp31655697p31693420.html
Sent from the Cocoon - Users mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org


Re: Streaming buffered image using Cocoon

Posted by Thomas Markus <t....@proventis.net>.
Hi,

thats pretty simple. Create a reader like this (creates a png with 
current timestamp in red):

public class TestImageReader extends AbstractReader {

     public String getMimeType() {
         return "image/png";
     }

     public void generate() throws IOException, SAXException, 
ProcessingException {
         String date = DateFormat.getDateTimeInstance().format(new Date());
         Font font = Font.decode(Font.SERIF).deriveFont(80.f);
         TextLayout textLayout = new TextLayout(date, font, new 
FontRenderContext(null, true, true));
         Rectangle2D bounds = textLayout.getBounds();
         BufferedImage image = new BufferedImage((int) 
bounds.getWidth(), (int) bounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = image.createGraphics();
         g.setColor(Color.RED);
         textLayout.draw(g, (float) -bounds.getX(), (float) -bounds.getY());
         g.dispose();
         ImageIO.write(image, "png", this.out);
     }

}

in your sitemap define and use this reader:
<map:components>
<map:readers>
<map:reader name="timeimg" src="test.TestImageReader" />
</map:readers>
</map:components>

...

<map:match pattern="time.jpg">
   <map:read type="timeimg" />
</map:match>


hth
Thomas


Am 21.05.2011 14:00, schrieb JeVeoy:
>
> First of all: thank you so much for helping me out, Steven&  Thomas!
>
> Then: I've tried my best to see if I could get a working example to run
> using either javax.imageio.ImageIO.write(RenderedImage, String,
> OutputStream) or AbstractReader.out, but to no luck. Googling didn't quite
> help either. I'm very sorry for my lack of knowledge... Hope you can forgive
> me.
>
> Pipeline for an image-reader is, as far as I know, defined in sitemap.xmap
> like this:
>
> <map:match pattern="*.jpg">
>    <map:read type="image" src="path/{1}.png" mime-type="image/png">
>    </map:read>
> </map:match>
>
> How can I make use of AbstractReader.out in this definition?


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org


Re: Streaming buffered image using Cocoon

Posted by JeVeoy <jo...@wisloff.org>.

Thomas Markus wrote:
> 
> hi,
> 
> create a reader and stream your image. see AbstractReader.out and beware 
> of org.apache.cocoon.util.BufferedOutputStream.realFlush()
> 
> regards
> Thomas
> 
>> You can use
>> javax.imageio.ImageIO.write(RenderedImage, String, OutputStream)
>> to write directly into an OutputStream.
>>
>> You just need to get the OutputStream from the original HTTP request.
>>
>> HTH,
>> Steven
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
> For additional commands, e-mail: users-help@cocoon.apache.org
> 
> 
> 


First of all: thank you so much for helping me out, Steven & Thomas!

Then: I've tried my best to see if I could get a working example to run
using either javax.imageio.ImageIO.write(RenderedImage, String,
OutputStream) or AbstractReader.out, but to no luck. Googling didn't quite
help either. I'm very sorry for my lack of knowledge... Hope you can forgive
me.

Pipeline for an image-reader is, as far as I know, defined in sitemap.xmap
like this:

<map:match pattern="*.jpg">
  <map:read type="image" src="path/{1}.png" mime-type="image/png">
  </map:read>
</map:match>

How can I make use of AbstractReader.out in this definition?
-- 
View this message in context: http://old.nabble.com/Streaming-buffered-image-using-Cocoon-tp31655697p31670434.html
Sent from the Cocoon - Users mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org


Re: Streaming buffered image using Cocoon

Posted by Thomas Markus <t....@proventis.net>.
hi,

create a reader and stream your image. see AbstractReader.out and beware 
of org.apache.cocoon.util.BufferedOutputStream.realFlush()

regards
Thomas

> You can use
> javax.imageio.ImageIO.write(RenderedImage, String, OutputStream)
> to write directly into an OutputStream.
>
> You just need to get the OutputStream from the original HTTP request.
>
> HTH,
> Steven


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org


Re: Streaming buffered image using Cocoon

Posted by Steven Dolg <st...@indoqa.com>.
Am 19.05.2011 15:49, schrieb JeVeoy:
> I'm using Cocoon 2.2
>
> Case
>
> I would like to merge 2 images in PNG format on the server (buffered image)
> and then send the result back as a stream. Thus NOT saving the image on the
> server.
>
> As an example you could have a nice background picture of a field. The field
> (background image) changes according to season (winter, spring etc). The
> user has the option to choose what kind of tree to display on the field.
> He/she can select from a wide range of fruit trees like olive, apple etc.
>
> One option is to save all possible combinations as unique images on the
> server. I want to avoid this since the total number of combinations could
> get very high. It could get really difficult to maintain changes to
> backgrounds and trees. I would prefer to have 4 background images and 1
> image for every tree and combine these on the fly and present the result to
> the user without saving it on the server.
>
> Merge
>
> The merge itself is ok and works like a charm. Code written in Java uses
> java.awt.image.BufferedImage.
>
> Code
>
>      // base path of the images
>      File path = new File("C:/images");
>
>      // load source images
>      BufferedImage image = ImageIO.read(new File(path,
> "background_summer.png"));
>      BufferedImage overlay = ImageIO.read(new File(path, "tree_apple.png"));
>
>      // create the new image, canvas size is the max of both image sizes
>      int w = Math.max(image.getWidth(), overlay.getWidth());
>      int h = Math.max(image.getHeight(), overlay.getHeight());
>      BufferedImage combined = new BufferedImage(w, h,
> BufferedImage.TYPE_INT_ARGB);
>
>      // paint both images, preserving the alpha channels
>      Graphics g = combined.getGraphics();
>      g.drawImage(image, 0, 0, null);
>      g.drawImage(overlay, 0, 0, null);
>
>      // Save as new image
>      ImageIO.write(combined, "PNG", new File(path, "combined.png"));
>
> Using PNG format on image enable result with transparency.
>
> And here comes the challenge. Instead of saving image to disc and then
> referring to it in sitemap.xmap I would like to return the stream.

You can use
javax.imageio.ImageIO.write(RenderedImage, String, OutputStream)
to write directly into an OutputStream.

You just need to get the OutputStream from the original HTTP request.

HTH,
Steven

> As far as my understanding goes I would have to catch the request in
> sitemap.xmap (map:match pattern="getTree.*") and then call flow script using
> map:call function with map:parameter. In the flow-function I would get the
> parameter (in this case, the type of tree to display) and call the
> java-function as written in the above snippet.
>
> But how do I return a stream as a PNG image to the browser using
> sitemap.xmap?


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org