Parsing jpegs

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Parsing jpegs

Post by AnonymousTipster »

Ok, i'm working on jpeg support for my engine, and I'm quite close, but I'm having trouble reading the jpeg into a char list. Here is my code so far:

Code: Select all

unsigned char *loadJPEGfromfile(const char *filename,int widp, int heip)
{
  //this function loads into RAM, not VRAM
	int bufferwidth;
	int pixelformat;
	int unknown;
	unsigned int sig_read = 0;
	int bit_depth, color_type, interlace_type, x, y;
	u32* line;
	FILE *fp;
	int wid, hei;
	wid = RoundUpPow2(widp);
	hei = RoundUpPow2(heip);
	int maxWid, maxHei;

	/* This struct contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;
  /* We use our private extension JPEG error handler.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct my_error_mgr jerr;
  /* More stuff */
  FILE * infile;		/* source file */
  JSAMPARRAY buffer;		/* Output row buffer */
  int row_stride;		/* physical row width in output buffer */

  /* In this example we want to open the input file before doing anything else,
   * so that the setjmp() error recovery below can assume the file is open.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to read binary files.
   */

  if ((infile = fopen(filename, "rb")) == NULL) {
    //printf(stderr, "can't open %s\n", filename);
    return 0;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * We need to clean up the JPEG object, close the input file, and return.
     */
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return 0;
  }
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.doc for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  (void) jpeg_start_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* We may need to do some setup of our own at this point before reading
   * the data.  After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   * In this example, we need to make an output work buffer of the right size.
   */ 
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;
  /* Make a one-row-high sample array that will go away when done with image */
  buffer = (*cinfo.mem->alloc_sarray)
		((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */
  int c;
  x=0;
  y=0;	
  unsigned char *output,*outptr;
  
  size_t sizediff;
  sizediff = wid*hei*4;
  int tsize = wid*hei;
  c=0;
  ramaddr = (unsigned int)memalign(16,sizediff);
  outptr = output = (unsigned char *)ramaddr;

  for &#40;y=0;y<hei;y++&#41; &#123;
	  &#40;void&#41; jpeg_read_scanlines&#40;&cinfo, buffer, 1&#41;;
  for &#40;x=0;x<wid;x++&#41;&#123;
	  int r = 0xff; 
	  int g = 0xff; 
	  int b = 0xff;
			
		outptr&#91;&#40;int&#41;&#40;&#40;x*4&#41;+&#40;y*wid*4&#41;&#41;&#93; = buffer&#91;&#40;x*3&#41;&#93;;
		outptr&#91;&#40;int&#41;&#40;&#40;x*4&#41;+&#40;y*wid*4&#41;+1&#41;&#93; = buffer&#91;&#40;x*3&#41;+1&#93;;
		outptr&#91;&#40;int&#41;&#40;&#40;x*4&#41;+&#40;y*wid*4&#41;+2&#41;&#93; = buffer&#91;&#40;x*3&#41;+2&#93;;
		outptr&#91;&#40;int&#41;&#40;&#40;x*4&#41;+&#40;y*wid*4&#41;+3&#41;&#93; = 0xff;
  &#125;
  &#125;

  /*
  while &#40;cinfo.output_scanline < cinfo.output_height&#41; &#123;

    &#40;void&#41; jpeg_read_scanlines&#40;&cinfo, buffer, 1&#41;;

  &#125;*/

  /* Step 7&#58; Finish decompression */

  &#40;void&#41; jpeg_finish_decompress&#40;&cinfo&#41;;
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8&#58; Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress&#40;&cinfo&#41;;

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above.  &#40;Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...&#41;
   */
  fclose&#40;infile&#41;;

  /* At this point you may want to check to see whether any corrupt-data
   * warnings occurred &#40;test whether jerr.pub.num_warnings is nonzero&#41;.
   */

  return output; 
This code currently returns a black texture with a single red strip of pixels down the side. I have attempted to find a sample/source for a loader, but all apps that I found use SDL_image, rather than the libjpeg. The above code was mostly lifted from the example in the jpeglib, exept i've changed the code around "jpeg_read_scanlines(&cinfo, buffer, 1);". When reading into the buffer, does it add onto the end, or start over, and how can I extract the RGB data into a char* list?

Thanks.
Shine
Posts: 728
Joined: Fri Dec 03, 2004 12:10 pm
Location: Germany

Re: Parsing jpegs

Post by Shine »

AnonymousTipster wrote:This code currently returns a black texture with a single red strip of pixels down the side. I have attempted to find a sample/source for a loader, but all apps that I found use SDL_image, rather than the libjpeg.
Your code looks ok. You can find another example at http://svn.ps2dev.org/filedetails.php?r ... rev=0&sc=0 in the loadJpegImage function. When jpeg_read_scanlines starts always at the beginning of the buffer. Extracting the RGB data to your char* array depends on your pixelformat.
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Post by AnonymousTipster »

Ok, by changing

Code: Select all

&#40;void&#41; jpeg_read_scanlines&#40;&cinfo, buffer, 1&#41;;
to

Code: Select all

&#40;void&#41; jpeg_read_scanlines&#40;&cinfo, &buffer, 1&#41;;
Allows the function to read the jpeg. It's appearing full height, but a quarter width, so i'll look into that tomorrow, but at least it's loading the data.
It's odd that the example has the code wrong, but at least it works now, thanks Shine.
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Post by AnonymousTipster »

Hmm. This is very odd. It seems to be finding the jpeg alright, but it only loads a quarter of it. The odd thing is that it loads 1 out of every 4 pixels. This means I end up with an image that is full height, but only a quarter width, all squashed to the left.
The scanline function appears to read one pixel, then jump the next three, then read one etc.
I can't see what could be causing this problem.
What can I do?
AyAn4m1
Posts: 15
Joined: Mon Sep 26, 2005 2:45 am

Post by AyAn4m1 »

Tell me if I'm wrong here... But in outptr you're using x*4 and in the buffer it's x*3... Is there a reason for this?
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Post by AnonymousTipster »

My texture is in 32bit RGBA format (0xff,0xff,0xff,0xff) and the jpeg is in 24bit RGB (0xff,0xff,0xff). Hence 4 - 3 ratio.

I'm hoping I find something wrong in my code, but it seems like read_scanlines() is returning the incorrect amount of data, but this is very unlikely. I continue to scour my code for the problem.
I also tried porting the LUA jpeg code, but got 4 greyscale images (each a quarter width by quarter height) all at the top of the image, with the rest of the image below blank.
I also tried encoding a different jpeg, which didn't seem to work.
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Post by AnonymousTipster »

What format does (void) jpeg_read_scanlines(&cinfo, buffer, 1); return? I've been treating it as an unsigned char in the format RGB 0xff,0xff,0xff. This means there are 3 chars for each pixel. Is this right, or is it 0xffffff or another format?
Shine
Posts: 728
Joined: Fri Dec 03, 2004 12:10 pm
Location: Germany

Post by Shine »

AnonymousTipster wrote:I also tried porting the LUA jpeg code, but got 4 greyscale images (each a quarter width by quarter height) all at the top of the image, with the rest of the image below blank.
Do you have a grayscale JPEG image? I didn't tested this branch of the JPEG load code, please post a link to your image, then I'll check it.
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Post by AnonymousTipster »

This is the image i've been using to test:
Image
I can load it in full colour, but only 1/4 width (quashed to the left).
rinco
Posts: 255
Joined: Fri Jan 21, 2005 2:12 pm
Location: Canberra, Australia

Post by rinco »

Does RoundUpPow2 not round up if already a power of 2? Using a width of 512 instead of 128 might result in a 1/4 horizontal squash.

And I've seen a lot of roundUpPowerOfTwo functions floating around... the best being the one mrbrown submitted in SDL:

Code: Select all

static inline int roundUpToPowerOfTwo &#40;int x&#41;
&#123;
    return 1 << &#40;32 - __builtin_allegrex_clz&#40;x - 1&#41;&#41;;
&#125;
Last edited by rinco on Sun Nov 27, 2005 1:03 am, edited 1 time in total.
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Post by AnonymousTipster »

I tried taking out the RoundUp function, so just using the passed values (which for the moment is 128x128 anyway), but that doesn't change anything, so I think the function is ok.
The code is here in case there is a problem with it:

Code: Select all

unsigned long RoundUpPow2&#40;unsigned long value&#41;
&#123;
    --value;
    value |= value >> 1;
    value |= value >> 2;
    value |= value >> 4;
    value |= value >> 8;
    value |= value >> 16;
    ++value;

    return value;
&#125;
Shine
Posts: 728
Joined: Fri Dec 03, 2004 12:10 pm
Location: Germany

Post by Shine »

AnonymousTipster wrote:This is the image i've been using to test:
Image
I can load it in full colour, but only 1/4 width (quashed to the left).
It works in Lua Player with this script:

Code: Select all

jpg = Image.load&#40;"basicJPEG.jpg"&#41;
screen&#58;blit&#40;0, 0, jpg&#41;
screen&#58;flip&#40;&#41;
while true do
	screen.waitVblankStart&#40;&#41;
	if Controls.read&#40;&#41;&#58;start&#40;&#41; then break end
end
Perhaps you have problems with blitting it? If you don't want to use Lua, perhaps you should take a look at the new cpplibs/libpsp2d in SVN, which fraca7 has created for using from Lua Player, Python and other programs, which wants to use simple 2D graphics. But it is very new, I don't know, if it is ready to use (but it should, because it is based on the old C functions from Lua Player).
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Post by AnonymousTipster »

Ok, thanks. I thought the problem was in my code somewhere, but finding it is proving tricky.
I'll look at the 2D library and see if that helps. Thanks.
I don't think the problem is in the blitting routine, because the PNG loader works perfectly, as does Freetype2.
ector
Posts: 195
Joined: Thu May 12, 2005 10:22 pm

Post by ector »

I'm wondering about the weird type of your "buffer" variable. Try to cast it into a char* perhaps?
http://www.dtek.chalmers.se/~tronic/PSPTexTool.zip Free texture converter for PSP with source. More to come.
AnonymousTipster
Posts: 197
Joined: Fri Jul 01, 2005 2:50 am

Post by AnonymousTipster »

Interesting, by changing

Code: Select all

JSAMPARRAY buffer;
to

Code: Select all

 unsigned char *buffer;
. I get a full length image, but only the red component. This is particularly odd because the documentation says that JSAMPARRAY should act exactly like unsigned char*, but it doesn't. Hmm.

OK, I've got it fixed now, I was using a different method which worked best with the JSAMP, but by changing it to the old method, it works perfectly with unsigned char*. Thanks ector!

Still puzzles me that JSAMPARRAY isn't what it's supposed to be, but at least it's working now.
Post Reply