3

edited "32-bit buffer" below means that it contains 32-bit rgba pixels.

I need to save image to file from 32-bit memory buffer. Code below works well for that when I use TIFF format. In this case saved image file is correct. But the same code doesn't work for JPEG and PNG. In these cases files are created, they have valid dimensions, color depth, but all pixels are black. GDAL functions don't signal any errors.

Some strings with error checking were omitted in the code below.

buf variable refers to 32-bit source buffer with ready image pixels.

To save to PNG format, I change driver name strDrv to "PNG" and file extension to .png correspondingly. For jpeg too.

union rgba_t {
    struct { unsigned char g, b, r, a; };
    unsigned c;
};

int cx = 500, cy = 500;
rgba_t buf = new rgba_t[cx * cy];
memset(buf, 0xff, cx * cy * sizeof(rgba_t));

GDALAllRegister();

GDALDataset     *pDs, *pDsMem;
char            **opt = NULL;
GDALDriver      *pDriver, *pDriverMem;
int             nBands = 3;
char            *strDrv = "GTiff",
                *fpath = "c:\\tmp\\o.tiff";

pDriverMem = GetGDALDriverManager()->GetDriverByName("MEM");
pDsMem = pDriverMem->Create(fpath, cx, cy, nBands, GDT_Byte, opt);

pDriver = GetGDALDriverManager()->GetDriverByName(strDrv);
pDs = pDriver->CreateCopy(fpath, pDsMem, FALSE, opt, NULL, NULL);

GDALRasterBand  *pBandR = pDs->GetRasterBand(1),
                *pBandG = pDs->GetRasterBand(2), 
                *pBandB = pDs->GetRasterBand(3);

int szScan = cx * sizeof(rgba_t);

CPLErr err; 
err = pBandR->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 2, cx, cy, GDT_Byte, 4, szScan);
err = pBandG->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 1, cx, cy, GDT_Byte, 4, szScan);
err = pBandB->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 0, cx, cy, GDT_Byte, 4, szScan);

GDALClose(pDs);
delete[] buf;
2
  • It looks like you are using CreateCopy correctly, but there could be something wrong with writing the raster band data. What were you expecting to see, if not a black 500x500 image? – Mike T Feb 12 '15 at 2:33
  • @Mike T - I fill my image buffer with a picture in my real code. In the example above I fill it with white color (see memset() call). The result is 500x500 black image. But I hope that I've already found my mistake: CreateCopy() for PNG and JPEG (not for TIFF) makes read-only Dataset and RasterIO() really doesn't work (but doesn't report error). I hope that I'll find and share the solution after some small tests. – Alexei Feb 12 '15 at 8:27
4

I've caught my error. May be it will be helpful for somebody.

CreateCopy() makes read-only Dataset for PNG and JPG, so RasterIO() doesn't write anything into dataset's bands. We have to call RasterIO for the memory Dataset. I've made tests for PNG, JPEG and TIFF, it works.

Create() call for "MEM" dataset doesn't allocate memory for the raster. Memory allocation takes place in RasterIO() calls.

We can activate driver and call CreateCopy() for destination format at the end, when image was completely formed in the memory dataset.

The final code can look like...

union rgba_t {
    struct { unsigned char g, b, r, a; };
    unsigned c;
};

int cx = 500, cy = 500;
rgba_t buf = new rgba_t[cx * cy];
memset(buf, 0xff, cx * cy * sizeof(rgba_t));

GDALAllRegister();

GDALDataset     *pDs, *pDsMem;
char            **opt = NULL;
GDALDriver      *pDriver, *pDriverMem;
int             nBands = 3;
char            *strDrv = "GTiff",
                *fpath = "c:\\tmp\\o.tiff";

pDriverMem = GetGDALDriverManager()->GetDriverByName("MEM");
pDsMem = pDriverMem->Create("", cx, cy, nBands, GDT_Byte, opt);

GDALRasterBand  *pBandR = pDsMem->GetRasterBand(1),
                *pBandG = pDsMem->GetRasterBand(2), 
                *pBandB = pDsMem->GetRasterBand(3);

int szScan = cx * sizeof(rgba_t);

CPLErr err; 
err = pBandR->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 2, cx, cy, GDT_Byte, 4, szScan);
err = pBandG->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 1, cx, cy, GDT_Byte, 4, szScan);
err = pBandB->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 0, cx, cy, GDT_Byte, 4, szScan);

pDriver = GetGDALDriverManager()->GetDriverByName(strDrv);
pDs = pDriver->CreateCopy(fpath, pDsMem, FALSE, opt, NULL, NULL);

GDALClose(pDsMem);
GDALClose(pDs);

delete[] buf;
3

Not all band types are supported with all format. If you look at the format support pages for jpg and png, 8 or 16 bit are needed. Float values could be simply rounded so that it does not results in an error, but gives some zeros or NoData. You should try to use "rescale" to fit in a Byte.

JPEG files are created using the "JPEG" driver code. Only Byte band types are supported, and only 1 and 3 band (RGB) configurations.

with PNG, Greyscale, pseudo-colored, Paletted, RGB and RGBA PNG files are supported as well as precisions of eight and sixteen bits per sample.

3
  • Thanks, radouxju. I create (i hope) 3 8-bit bands. I tried some variants including 1-band image too. – Alexei Feb 12 '15 at 7:45
  • @Alexei Please mark the answer as accepted if it solved your problem: gis.stackexchange.com/help/accepted-answer – AndreJ Feb 12 '15 at 7:48
  • @AndreJ OK, I'll do as soon as solution found. – Alexei Feb 12 '15 at 8:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.