Component_destruct(J2K_USER_DATA * data) { if (data) { ComponentImpl *impl = (ComponentImpl*) data; J2K_FREE(data); } }
J2KAPI(void) j2k_Writer_destruct(j2k_Writer **writer) { if (*writer) { if ((*writer)->iface && (*writer)->data) (*writer)->iface->destruct((*writer)->data); J2K_FREE(*writer); *writer = NULL; } }
j2k_Container_destruct(j2k_Container **container) { if (*container) { if ((*container)->iface && (*container)->data) (*container)->iface->destruct((*container)->data); J2K_FREE(*container); *container = NULL; } }
j2k_Component_destruct(j2k_Component **component) { if (*component) { if ((*component)->iface && (*component)->data) (*component)->iface->destruct((*component)->data); J2K_FREE(*component); *component = NULL; } }
OpenJPEGWriter_destruct(J2K_USER_DATA * data) { if (data) { OpenJPEGWriterImpl* const impl = (OpenJPEGWriterImpl*) data; OpenJPEG_cleanup(&impl->stream, &impl->codec, &impl->image); nrt_IOInterface_destruct(&impl->compressed); J2K_FREE(data); } }
OpenJPEGReader_destruct(J2K_USER_DATA * data) { if (data) { OpenJPEGReaderImpl* const impl = (OpenJPEGReaderImpl*) data; if (impl->io && impl->ownIO) { nrt_IOInterface_destruct(&impl->io); impl->io = NULL; } if(impl->container) { j2k_Container_destruct(&impl->container); impl->container = NULL; } J2K_FREE(data); } }
Container_destruct(J2K_USER_DATA * data) { if (data) { ContainerImpl *impl = (ContainerImpl*) data; if (impl->components) { nrt_Uint32 i = 0; for(; i < impl->nComponents; ++i) { j2k_Component *c = impl->components[i]; if (c) j2k_Component_destruct(&c); } impl->components = NULL; } J2K_FREE(data); } }
J2K_BOOL readFile(const char* filename, char **buf, nrt_Uint64 *bufSize, nrt_Error *error) { J2K_BOOL rc = J2K_TRUE; nrt_IOInterface *io = NULL; if (!(io = nrt_IOHandleAdapter_open(filename, NRT_ACCESS_READONLY, NRT_OPEN_EXISTING, error))) goto CATCH_ERROR; *bufSize = nrt_IOInterface_getSize(io, error); if (!(*buf = (char*)J2K_MALLOC(*bufSize))) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } if (!nrt_IOInterface_read(io, *buf, *bufSize, error)) { goto CATCH_ERROR; } goto CLEANUP; CATCH_ERROR: { rc = J2K_FALSE; if (*buf) J2K_FREE(*buf); *buf = NULL; } CLEANUP: { if (io) nrt_IOInterface_destruct(&io); } return rc; }
int main(int argc, char **argv) { int rc = 0; int argIt = 0; char *inName = NULL, *outName = NULL; nrt_Error error; j2k_Component *component = NULL; j2k_Container *container = NULL; j2k_Writer *writer = NULL; j2k_WriterOptions options; char *buf = NULL; nrt_Uint64 bufSize; nrt_IOInterface *outIO = NULL; nrt_Uint32 width, height, precision, tileWidth, tileHeight; for (argIt = 1; argIt < argc; ++argIt) { if (!inName) { inName = argv[argIt]; } else if (!outName) { outName = argv[argIt]; } } /* hardcoded for now... */ width = 128; height = 128; precision = 8; tileWidth = width; tileHeight = height; if (!inName || !outName) { nrt_Error_initf(&error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Usage: %s <raw-input> <output-j2k>", argv[0]); goto CATCH_ERROR; } if (!(component = j2k_Component_construct(width, height, precision, 0, 0, 0, 1, 1, &error))) { goto CATCH_ERROR; } if (!(container = j2k_Container_construct(width, height, 1, &component, tileWidth, tileHeight, J2K_TYPE_MONO, &error))) { goto CATCH_ERROR; } memset(&options, 0, sizeof(j2k_WriterOptions)); /* TODO set some options here */ if (!(writer = j2k_Writer_construct(container, &options, &error))) { goto CATCH_ERROR; } if (!readFile(inName, &buf, &bufSize, &error)) { goto CATCH_ERROR; } if (!j2k_Writer_setTile(writer, 0, 0, (nrt_Uint8*)buf, (nrt_Uint32)bufSize, &error)) { goto CATCH_ERROR; } if (!(outIO = nrt_IOHandleAdapter_open(outName, NRT_ACCESS_WRITEONLY, NRT_CREATE, &error))) goto CATCH_ERROR; if (!j2k_Writer_write(writer, outIO, &error)) goto CATCH_ERROR; goto CLEANUP; CATCH_ERROR: { nrt_Error_print(&error, stdout, "Exiting..."); rc = 1; } CLEANUP: { if (container) j2k_Container_destruct(&container); if (writer) j2k_Writer_destruct(&writer); if (buf) J2K_FREE(buf); if (outIO) nrt_IOInterface_destruct(&outIO); } return rc; }
OpenJPEGWriter_setTile(J2K_USER_DATA *data, nrt_Uint32 tileX, nrt_Uint32 tileY, const nrt_Uint8 *buf, nrt_Uint32 tileSize, nrt_Error *error) { OpenJPEGWriterImpl *impl = (OpenJPEGWriterImpl*) data; NRT_BOOL rc = NRT_SUCCESS; nrt_Uint32 xTiles, yTiles, tileIndex, width, height, tileWidth, tileHeight; nrt_Uint32 thisTileWidth, thisTileHeight, thisTileSize, nComponents, nBytes; nrt_Uint8* newTileBuf = NULL; xTiles = j2k_Container_getTilesX(impl->container, error); yTiles = j2k_Container_getTilesY(impl->container, error); width = j2k_Container_getWidth(impl->container, error); height = j2k_Container_getHeight(impl->container, error); tileWidth = j2k_Container_getTileWidth(impl->container, error); tileHeight = j2k_Container_getTileHeight(impl->container, error); nComponents = j2k_Container_getNumComponents(impl->container, error); nBytes = (j2k_Container_getPrecision(impl->container, error) - 1) / 8 + 1; tileIndex = tileY * xTiles + tileX; memset(error->message, 0, NRT_MAX_EMESSAGE); if(!opj_set_error_handler(impl->codec, OpenJPEG_errorHandler, error)) { nrt_Error_init(error, "Unable to set OpenJPEG error handler", NRT_CTXT, NRT_ERR_UNK); goto CATCH_ERROR; } /* Check for edge case where we may have partial tile */ thisTileWidth = tileWidth; thisTileHeight = tileHeight; if (tileX == xTiles - 1 && width % tileWidth != 0) thisTileWidth = width % tileWidth; if (tileY == yTiles - 1 && height % tileHeight != 0) thisTileHeight = height % tileHeight; thisTileSize = thisTileWidth * thisTileHeight * nComponents * nBytes; if(thisTileSize != tileSize) tileSize = thisTileSize; if(thisTileWidth < tileWidth) { /* TODO: The current approach below only works for single band * imagery. For RGB data, I believe it is stored as all * red, then all green, then all blue, so we would need * a temp buffer rather than reusing the current buffer. */ if (nComponents != 1) { nrt_Error_init( error, "Partial tile width not implemented for multi-band", NRT_CTXT, NRT_ERR_UNK); goto CATCH_ERROR; } /* We have a tile that is wider than it "should" be * Need to create smaller buffer to pass to write function */ { OPJ_UINT32 ii; size_t srcOffset = 0; size_t destOffset = 0; const size_t srcStride = tileWidth * nBytes; const size_t destStride = thisTileWidth * nBytes; newTileBuf = (nrt_Uint8*) J2K_MALLOC(thisTileSize); if(!newTileBuf) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } for(ii = 0; ii < thisTileHeight; ++ii, srcOffset += srcStride, destOffset += destStride) memcpy(newTileBuf + destOffset, buf + srcOffset, destStride); buf = newTileBuf; } } if (!opj_write_tile(impl->codec, tileIndex, (OPJ_BYTE* )buf, tileSize, impl->stream)) { nrt_Error ioError; const nrt_Off currentPos = nrt_IOInterface_tell(impl->compressed, &ioError); const nrt_Off ioSize = nrt_IOInterface_getSize(impl->compressed, &ioError); if (NRT_IO_SUCCESS(currentPos) && NRT_IO_SUCCESS(ioSize) && currentPos + OPENJPEG_STREAM_SIZE >= ioSize) { /* The write failed because implStreamWrite() failed because * nrt_IOInterface_write() failed because we didn't have enough * room left in the buffer that we copy to prior to flushing out * to disk in OpenJPEGWriter_write(). The buffer is sized to the * uncompressed image size, so this only occurs if the compressed * image is actually larger than the uncompressed size. * TODO: Handle resizing the buffer on the fly when this occurs * inside implStreamWrite(). Long-term if we're able to thread * per tile, we won't have to reallocate nearly as much. */ nrt_Error_init(error, "Error writing tile: Compressed image is larger " "than uncompressed image", NRT_CTXT, NRT_ERR_INVALID_OBJECT); } /*nrt_Error_init(error, "Error writing tile", NRT_CTXT, NRT_ERR_INVALID_OBJECT);*/ goto CATCH_ERROR; } goto CLEANUP; CATCH_ERROR: { rc = NRT_FAILURE; } CLEANUP: { if(newTileBuf) J2K_FREE(newTileBuf); } return rc; }
J2KPRIV( NRT_BOOL) OpenJPEG_initImage(OpenJPEGWriterImpl *impl, j2k_WriterOptions *writerOps, nrt_Error *error) { NRT_BOOL rc = NRT_SUCCESS; nrt_Uint32 i, nComponents, height, width, tileHeight, tileWidth; nrt_Uint32 nBytes; j2k_Component *component = NULL; size_t uncompressedSize; int imageType; opj_cparameters_t encoderParams; opj_image_cmptparm_t *cmptParams; OPJ_COLOR_SPACE colorSpace; nComponents = j2k_Container_getNumComponents(impl->container, error); width = j2k_Container_getWidth(impl->container, error); height = j2k_Container_getHeight(impl->container, error); tileWidth = j2k_Container_getTileWidth(impl->container, error); tileHeight = j2k_Container_getTileHeight(impl->container, error); imageType = j2k_Container_getImageType(impl->container, error); /* Set up the encoder parameters. This defaults to lossless. */ /* TODO allow overrides somehow? */ opj_set_default_encoder_parameters(&encoderParams); /* For now we are enforcing lossless compression. If we have a better * way to allow overrides in the future, uncomment out the tcp_rates logic * below (tcp_rates[0] == 0 via opj_set_default_encoder_parameters()). * Also consider setting encoderParams.irreversible = 1; to use the * lossy DWT 9-7 instead of the reversible 5-3. */ /*if (writerOps && writerOps->compressionRatio > 0.0001) encoderParams.tcp_rates[0] = 1.0 / writerOps->compressionRatio; else encoderParams.tcp_rates[0] = 4.0; */ /* TODO: These two lines should not be necessary when using lossless * encoding but appear to be needed (at least in OpenJPEG 2.0) - * otherwise we get a seg fault. * The sample opj_compress.c is doing the same thing with a comment * indicating that it's a bug. */ ++encoderParams.tcp_numlayers; encoderParams.cp_disto_alloc = 1; if (writerOps && writerOps->numResolutions > 0) { encoderParams.numresolution = writerOps->numResolutions; /* Note, if this isn't set right (see below) it will error out */ } else { /* OpenJPEG defaults this to 6, but that causes the compressor to fail if the tile sizes are less than 2^6. So we adjust this down if necessary. */ const double logTwo = log(2); const OPJ_UINT32 minX = (OPJ_UINT32)floor(log(tileWidth) / logTwo); const OPJ_UINT32 minY = (OPJ_UINT32)floor(log(tileHeight) / logTwo); const OPJ_UINT32 minXY = (minX < minY) ? minX : minY; if (minXY < encoderParams.numresolution) { encoderParams.numresolution = minXY; } } /* Turn on tiling */ encoderParams.tile_size_on = 1; encoderParams.cp_tx0 = 0; encoderParams.cp_ty0 = 0; encoderParams.cp_tdx = tileWidth; encoderParams.cp_tdy = tileHeight; if (!(cmptParams = (opj_image_cmptparm_t*)J2K_MALLOC(sizeof( opj_image_cmptparm_t) * nComponents))) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(cmptParams, 0, sizeof(opj_image_cmptparm_t) * nComponents); for(i = 0; i < nComponents; ++i) { component = j2k_Container_getComponent(impl->container, i, error); cmptParams[i].w = j2k_Component_getWidth(component, error); cmptParams[i].h = j2k_Component_getHeight(component, error); cmptParams[i].prec = j2k_Component_getPrecision(component, error); cmptParams[i].x0 = j2k_Component_getOffsetX(component, error); cmptParams[i].y0 = j2k_Component_getOffsetY(component, error); cmptParams[i].dx = j2k_Component_getSeparationX(component, error); cmptParams[i].dy = j2k_Component_getSeparationY(component, error); cmptParams[i].sgnd = j2k_Component_isSigned(component, error); } nBytes = (j2k_Container_getPrecision(impl->container, error) - 1) / 8 + 1; uncompressedSize = width * height * nComponents * nBytes; /* this is not ideal, but there is really no other way */ if (!(impl->compressedBuf = (char*)J2K_MALLOC(uncompressedSize))) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } if (!(impl->compressed = nrt_BufferAdapter_construct(impl->compressedBuf, uncompressedSize, 1, error))) { goto CATCH_ERROR; } if (!(impl->stream = OpenJPEG_createIO(impl->compressed, &impl->userData, 0, 0, error))) { goto CATCH_ERROR; } switch(imageType) { case J2K_TYPE_RGB: colorSpace = OPJ_CLRSPC_SRGB; break; default: colorSpace = OPJ_CLRSPC_GRAY; } if (!(impl->codec = opj_create_compress(OPJ_CODEC_J2K))) { nrt_Error_init(error, "Error creating OpenJPEG codec", NRT_CTXT, NRT_ERR_INVALID_OBJECT); goto CATCH_ERROR; } if (!(impl->image = opj_image_tile_create(nComponents, cmptParams, colorSpace))) { nrt_Error_init(error, "Error creating OpenJPEG image", NRT_CTXT, NRT_ERR_INVALID_OBJECT); goto CATCH_ERROR; } /* for some reason we must also explicitly specify these in the image... */ impl->image->numcomps = nComponents; impl->image->x0 = 0; impl->image->y0 = 0; impl->image->x1 = width; impl->image->y1 = height; impl->image->color_space = colorSpace; memset(error->message, 0, NRT_MAX_EMESSAGE); if(!opj_set_error_handler(impl->codec, OpenJPEG_errorHandler, error)) { nrt_Error_init(error, "Unable to set OpenJPEG error handler", NRT_CTXT, NRT_ERR_UNK); goto CATCH_ERROR; } if (!opj_setup_encoder(impl->codec, &encoderParams, impl->image)) { /*nrt_Error_init(error, "Error setting up OpenJPEG decoder", NRT_CTXT, NRT_ERR_INVALID_OBJECT);*/ goto CATCH_ERROR; } if (!opj_start_compress(impl->codec, impl->image, impl->stream)) { /*nrt_Error_init(error, "Error starting OpenJPEG compression", NRT_CTXT, NRT_ERR_INVALID_OBJECT);*/ goto CATCH_ERROR; } goto CLEANUP; CATCH_ERROR: { rc = NRT_FAILURE; } CLEANUP: { if (cmptParams) J2K_FREE(cmptParams); } return rc; }