J2KAPI(j2k_Container*) j2k_Container_construct(nrt_Uint32 gridWidth, nrt_Uint32 gridHeight, nrt_Uint32 numComponents, j2k_Component** components, nrt_Uint32 tileWidth, nrt_Uint32 tileHeight, int imageType, nrt_Error *error) { j2k_Container *container = NULL; ContainerImpl *impl = NULL; container = (j2k_Container*) J2K_MALLOC(sizeof(j2k_Container)); if (!container) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(container, 0, sizeof(j2k_Container)); /* create the Container interface */ impl = (ContainerImpl *) J2K_MALLOC(sizeof(ContainerImpl)); if (!impl) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(impl, 0, sizeof(ContainerImpl)); container->data = impl; container->iface = &ContainerInterface; impl->gridWidth = gridWidth; impl->gridHeight = gridHeight; impl->nComponents = numComponents; impl->components = components; impl->tileWidth = tileWidth; impl->tileHeight = tileHeight; impl->xTiles = gridWidth / tileWidth + (gridWidth % tileWidth == 0 ? 0 : 1); impl->yTiles = gridHeight / tileHeight + (gridHeight % tileHeight == 0 ? 0 : 1); impl->imageType = imageType; return container; CATCH_ERROR: { if (container) { j2k_Container_destruct(&container); } return NULL; } }
J2KAPI(j2k_Component*) j2k_Component_construct(nrt_Uint32 width, nrt_Uint32 height, nrt_Uint32 precision, NRT_BOOL isSigned, nrt_Uint32 offsetX, nrt_Uint32 offsetY, nrt_Uint32 separationX, nrt_Uint32 separationY, nrt_Error *error) { j2k_Component *component = NULL; ComponentImpl *impl = NULL; component = (j2k_Component*) J2K_MALLOC(sizeof(j2k_Component)); if (!component) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(component, 0, sizeof(j2k_Component)); /* create the Component interface */ impl = (ComponentImpl *) J2K_MALLOC(sizeof(ComponentImpl)); if (!impl) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(impl, 0, sizeof(ComponentImpl)); component->data = impl; component->iface = &ComponentInterface; impl->width = width; impl->height = height; impl->precision = precision; impl->isSigned = isSigned; impl->x0 = offsetX; impl->y0 = offsetY; impl->xSeparation = separationX; impl->ySeparation = separationY; return component; CATCH_ERROR: { if (component) { j2k_Component_destruct(&component); } return NULL; } }
J2KAPI(j2k_Reader*) j2k_Reader_openIO(nrt_IOInterface *io, nrt_Error *error) { OpenJPEGReaderImpl *impl = NULL; j2k_Reader *reader = NULL; /* create the Reader interface */ impl = (OpenJPEGReaderImpl *) J2K_MALLOC(sizeof(OpenJPEGReaderImpl)); if (!impl) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(impl, 0, sizeof(OpenJPEGReaderImpl)); reader = (j2k_Reader *) J2K_MALLOC(sizeof(j2k_Reader)); if (!reader) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(reader, 0, sizeof(j2k_Reader)); reader->data = impl; reader->iface = &ReaderInterface; /* initialize the interfaces */ impl->io = io; impl->ioOffset = nrt_IOInterface_tell(io, error); if (!OpenJPEG_readHeader(impl, error)) { goto CATCH_ERROR; } return reader; CATCH_ERROR: { if (reader) { j2k_Reader_destruct(&reader); } else if (impl) { OpenJPEGReader_destruct((J2K_USER_DATA*) impl); } return NULL; } }
NRTAPI(nrt_IOHandle) nrt_IOHandle_create(const char *fname, nrt_AccessFlags access, nrt_CreationFlags creation, nrt_Error * error) { HANDLE handle; if (access & NRT_ACCESS_WRITEONLY) { WIN32_FIND_DATA findData; handle = FindFirstFile(fname, &findData); if (handle != INVALID_HANDLE_VALUE) { creation |= TRUNCATE_EXISTING; FindClose(handle); } } handle = CreateFile(fname, access, FILE_SHARE_READ, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_OPENING_FILE); } return handle; }
NRTAPI(nrt_ListNode *) nrt_ListNode_construct(nrt_ListNode * prev, nrt_ListNode * next, NRT_DATA * data, nrt_Error * error) { nrt_ListNode *node = (nrt_ListNode *) NRT_MALLOC(sizeof(nrt_ListNode)); if (node == NULL) { /* Init the error with the string value of errno */ nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); /* Return if we have a problem */ return NULL; } /* Attention!! Achtung!! This is a data pointer copy, */ /* not copying an object. That means that YOU the user */ /* of this API MUST allocate it yourself */ /* And, of course, YOU must delete it as well!! */ node->data = data; /* Attach up our nodes */ node->next = next; node->prev = prev; /* Return the new node */ return node; }
J2KAPI(j2k_Writer*) j2k_Writer_construct(j2k_Container *container, j2k_WriterOptions *writerOps, nrt_Error *error) { j2k_Writer *writer = NULL; OpenJPEGWriterImpl *impl = NULL; writer = (j2k_Writer*) J2K_MALLOC(sizeof(j2k_Container)); if (!writer) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(writer, 0, sizeof(j2k_Writer)); /* create the Writer interface */ impl = (OpenJPEGWriterImpl *) J2K_MALLOC(sizeof(OpenJPEGWriterImpl)); if (!impl) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(impl, 0, sizeof(OpenJPEGWriterImpl)); impl->container = container; if (!(OpenJPEG_initImage(impl, writerOps, error))) { goto CATCH_ERROR; } writer->data = impl; writer->iface = &WriterInterface; return writer; CATCH_ERROR: { if (writer) { j2k_Writer_destruct(&writer); } return NULL; } }
NRTAPI(nrt_DLL *) nrt_DLL_construct(nrt_Error * error) { nrt_DLL *dll = (nrt_DLL *) NRT_MALLOC(sizeof(nrt_DLL)); if (!dll) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); } dll->libname = NULL; dll->lib = NULL; return dll; }
NRTPROT(nrt_IntStack *) nrt_IntStack_construct(nrt_Error * error) { nrt_IntStack *stk = (nrt_IntStack *) NRT_MALLOC(sizeof(nrt_IntStack)); if (!stk) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); return NULL; } stk->sp = -1; return stk; }
NRTAPI(nrt_DateTime *) nrt_DateTime_fromString(const char *string, const char *format, nrt_Error * error) { struct tm t; nrt_DateTime *dateTime = NULL; double millis = 0.0; /* NOTE: _NRT_strptime() does not use the tm_isdst flag at all. */ t.tm_isdst = -1; if (!_NRT_strptime(string, format, &t, &millis)) { nrt_Error_initf(error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Unknown error caused by the call to strptime with string [%s] and format string [%s]", string, format); return NULL; } /* Create a DateTime object */ dateTime = (nrt_DateTime *) NRT_MALLOC(sizeof(nrt_DateTime)); if (!dateTime) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); return NULL; } /* Initialize it from the tm struct * TODO: Update _NRT_strptime() to just use a DateTime directly */ dateTime->year = t.tm_year + 1900; /* 0-based so add 1 */ dateTime->month = t.tm_mon + 1; dateTime->dayOfMonth = t.tm_mday; dateTime->dayOfWeek = t.tm_wday + 1; dateTime->dayOfYear = t.tm_yday + 1; dateTime->hour = t.tm_hour; dateTime->minute = t.tm_min; dateTime->second = t.tm_sec + millis / 1000.0; /* Compute the # of milliseconds */ if (!nrt_DateTime_updateMillis(dateTime, error)) { NRT_FREE(dateTime); return NULL; } return dateTime; }
NRTAPI(nrt_List *) nrt_List_construct(nrt_Error * error) { /* New allocate a list */ nrt_List *l; l = (nrt_List *) NRT_MALLOC(sizeof(nrt_List)); if (!l) { /* Initialize the error and return NULL */ nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); return NULL; } /* Null-initialize the link pointers */ l->first = l->last = NULL; return l; }
NRTAPI(nrt_DateTime *) nrt_DateTime_fromMillis(double millis, nrt_Error * error) { nrt_DateTime *dt = NULL; dt = (nrt_DateTime *) NRT_MALLOC(sizeof(nrt_DateTime)); if (!dt) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); return NULL; } nrt_DateTime_setTimeInMillis(dt, millis, error); return dt; }
NRTAPI(NRT_BOOL) nrt_IOHandle_read(nrt_IOHandle handle, char *buf, size_t size, nrt_Error * error) { static const DWORD MAX_READ_SIZE = (DWORD)-1; size_t bytesRead = 0; size_t bytesRemaining = size; while (bytesRead < size) { /* Determine how many bytes to read */ const DWORD bytesToRead = (bytesRemaining > MAX_READ_SIZE) ? MAX_READ_SIZE : (DWORD)bytesRemaining; /* Read from file */ DWORD bytesThisRead = 0; if (!ReadFile(handle, buf + bytesRead, bytesToRead, &bytesThisRead, NULL)) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_READING_FROM_FILE); return NRT_FAILURE; } else if (bytesThisRead == 0) { // ReadFile does not fail when finding the EOF -- // instead it reports 0 bytes read, so this stops an infinite loop // from Unexpected EOF nrt_Error_init(error, "Unexpected end of file", NRT_CTXT, NRT_ERR_READING_FROM_FILE); return NRT_FAILURE; } bytesRead += bytesThisRead; bytesRemaining -= bytesThisRead; } return NRT_SUCCESS; }
NRTAPI(NRT_BOOL) nrt_DLL_load(nrt_DLL * dll, const char *libname, nrt_Error * error) { dll->libname = (char *) NRT_MALLOC(strlen(libname) + 1); if (!dll->libname) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); return NRT_FAILURE; } strcpy(dll->libname, libname); dll->lib = dlopen(libname, RTLD_LAZY); if (!dll->lib) { nrt_Error_init(error, dlerror(), NRT_CTXT, NRT_ERR_LOADING_DLL); NRT_FREE(dll->libname); dll->libname = NULL; return NRT_FAILURE; } return NRT_SUCCESS; }
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; }
NRTAPI(NRT_BOOL) nrt_IOHandle_write(nrt_IOHandle handle, const char *buf, size_t size, nrt_Error * error) { static const DWORD MAX_WRITE_SIZE = (DWORD)-1; size_t bytesRemaining = size; size_t bytesWritten = 0; while (bytesWritten < size) { /* Determine how many bytes to write */ const DWORD bytesToWrite = (bytesRemaining > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : (DWORD)bytesRemaining; /* Write the data */ DWORD bytesThisWrite = 0; if (!WriteFile(handle, buf + bytesWritten, bytesToWrite, &bytesThisWrite, NULL)) { /* If the function failed, we want to get the last error */ nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_WRITING_TO_FILE); /* And fail */ return NRT_FAILURE; } /* Otherwise, we want to accumulate this write until we are done */ bytesRemaining -= bytesThisWrite; bytesWritten += bytesThisWrite; } return NRT_SUCCESS; }
NRTAPI(nrt_List *) nrt_Utils_splitString(char *str, unsigned int max, nrt_Error * error) { unsigned int count = 0; nrt_List *parts; char *op, *cur, *end; size_t strLen; parts = nrt_List_construct(error); if (!parts) return NULL; strLen = strlen(str); end = str + strLen; op = str; if (max == 1) { char *val = NRT_MALLOC(strLen + 1); if (!val) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); return NULL; } memset(val, 0, strLen + 1); memcpy(val, str, strLen); nrt_List_pushBack(parts, val, error); } else { /* strtok is not thread safe */ while (op < end) { char *val = NULL; int sz; /* skip past white space */ while (isspace(*op) && op < end) ++op; cur = op; while (!isspace(*op) && op < end) ++op; if (cur == op) break; sz = op - cur; val = NRT_MALLOC(sz + 1); if (!val) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); return NULL; } memset(val, 0, sz + 1); memcpy(val, cur, sz); nrt_List_pushBack(parts, val, error); count++; /* check the count limit */ if (max != 0 && count == (max - 1) && op < end) { /* push on the rest of the string - skip spaces first */ while (isspace(*op) && op < end) ++op; if (op < end) { sz = end - op; val = NRT_MALLOC(sz + 1); if (!val) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); return NULL; } memset(val, 0, sz + 1); memcpy(val, op, sz); nrt_List_pushBack(parts, val, error); } break; } } } return parts; }
NRTAPI(NRT_BOOL) nrt_DateTime_formatMillis(double millis, const char *format, char *outBuf, size_t maxSize, nrt_Error * error) { time_t timeInSeconds; double fractSeconds; struct tm t; char *newFmtString = NULL; const char *endString = NULL; size_t begStringLen = 0; size_t formatLength; size_t startIndex = 0; size_t i, j; NRT_BOOL found = 0; timeInSeconds = (time_t) (millis / 1000); t = *gmtime(&timeInSeconds); fractSeconds = (millis / 1000.0) - timeInSeconds; /* Search for "%...S" string */ formatLength = strlen(format); for (i = 0; i < formatLength && !found; ++i) { if (format[i] == '%') { startIndex = i; for (j = startIndex + 1; j < formatLength; ++j) { if (format[j] == '%') { break; } if (format[j] == 'S') { found = 1; formatLength = j - startIndex + 1; begStringLen = startIndex; endString = format + j + 1; } } } } /* If we found a "%...S" string, parse it */ /* to find out how many decimal places to use */ if (found) { int decimalPlaces = 0; /* Figure out how many decimal places we need... */ for (i = startIndex + 1; i < startIndex + (formatLength - 1); ++i) { if (format[i] == '.') { /* The digits that follow should be */ /* the number of decimal places */ sscanf(format + i + 1, "%d", &decimalPlaces); } } if (decimalPlaces > 0) { char buf[256]; size_t newFmtLen = 0; size_t bufIdx = 0; size_t endStringLen = endString ? strlen(endString) : 0; newFmtLen = begStringLen + 1; newFmtString = (char *) NRT_MALLOC(newFmtLen); if (!newFmtString) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } memset(newFmtString, 0, newFmtLen); if (begStringLen > 0) { /* do the first part of the format */ strncpy(newFmtString, format, begStringLen); if (strftime(outBuf, maxSize, newFmtString, &t) == 0) { nrt_Error_initf(error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Unknown error caused by the call to strftime with format string: [%s]", format); goto CATCH_ERROR; } bufIdx = strlen(outBuf); } /* do the seconds - separately */ memset(buf, 0, 256); if (strftime(buf, 256, "%S", &t) == 0) { nrt_Error_initf(error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Unknown error caused by the call to strftime with format string: [%s]", format); goto CATCH_ERROR; } if (strlen(buf) + bufIdx + 1 > maxSize) { nrt_Error_initf(error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Format string will cause buffer to overflow: [%s]", format); goto CATCH_ERROR; } /* tack it on the end */ strcpy(outBuf + bufIdx, buf); bufIdx = strlen(outBuf); memset(buf, 0, 256); NRT_SNPRINTF(buf, 256, "%.*f", decimalPlaces, fractSeconds); if (strlen(buf) + bufIdx + 1 > maxSize) { nrt_Error_initf(error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Format string will cause buffer to overflow: [%s]", format); goto CATCH_ERROR; } /* tack on the fractional seconds - spare the leading 0 */ strcpy(outBuf + bufIdx, buf + 1); bufIdx = strlen(outBuf); if (endStringLen > 0) { /* tack on the end part */ memset(buf, 0, 256); if (strftime(buf, 256, endString, &t) == 0) { nrt_Error_initf(error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Unknown error caused by the call to strftime with format string: [%s]", format); goto CATCH_ERROR; } if (strlen(buf) + bufIdx + 1 > maxSize) { nrt_Error_initf(error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Format string will cause buffer to overflow: [%s]", format); goto CATCH_ERROR; } strcpy(outBuf + bufIdx, buf); } } } if (newFmtString == NULL) { if (strftime (outBuf, maxSize, newFmtString != NULL ? newFmtString : format, &t) == 0) { nrt_Error_initf(error, NRT_CTXT, NRT_ERR_INVALID_OBJECT, "Unknown error caused by the call to strftime with format string: [%s]", newFmtString != NULL ? newFmtString : format); goto CATCH_ERROR; } } else NRT_FREE(newFmtString); return NRT_SUCCESS; CATCH_ERROR: if (newFmtString) NRT_FREE(newFmtString); return NRT_FAILURE; }
OpenJPEG_readHeader(OpenJPEGReaderImpl *impl, nrt_Error *error) { opj_stream_t *stream = NULL; opj_image_t *image = NULL; opj_codec_t *codec = NULL; opj_codestream_info_v2_t* codeStreamInfo = NULL; NRT_BOOL rc = NRT_SUCCESS; OPJ_UINT32 tileWidth, tileHeight; OPJ_UINT32 imageWidth, imageHeight; if (!OpenJPEG_setup(impl, &stream, &codec, error)) { goto CATCH_ERROR; } if (!opj_read_header(stream, codec, &image)) { /*nrt_Error_init(error, "Error reading header", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } codeStreamInfo = opj_get_cstr_info(codec); if (!codeStreamInfo) { /*nrt_Error_init(error, "Error reading code stream", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } tileWidth = codeStreamInfo->tdx; tileHeight = codeStreamInfo->tdy; /* sanity checking */ if (!image) { nrt_Error_init(error, "NULL image after reading header", NRT_CTXT, NRT_ERR_UNK); goto CATCH_ERROR; } if (image->x0 >= image->x1 || image->y0 >= image->y1) { nrt_Error_init(error, "Invalid image offsets", NRT_CTXT, NRT_ERR_UNK); goto CATCH_ERROR; } if (image->numcomps == 0) { nrt_Error_init(error, "No image components found", NRT_CTXT, NRT_ERR_UNK); goto CATCH_ERROR; } /* TODO: We need special handling that's not implemented in readTile() to * accommodate partial tiles with more than one band. */ imageWidth = image->x1 - image->x0; imageHeight = image->y1 - image->y0; if (image->numcomps > 1 && (imageWidth % tileWidth != 0 || imageHeight % tileHeight != 0)) { nrt_Error_init(error, "No image components found", NRT_CTXT, NRT_ERR_UNK); goto CATCH_ERROR; } if (!impl->container) { /* initialize the container */ nrt_Uint32 idx; j2k_Component **components = NULL; int imageType; if (!(components = (j2k_Component**)J2K_MALLOC( sizeof(j2k_Component*) * image->numcomps))) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } for(idx = 0; idx < image->numcomps; ++idx) { opj_image_comp_t cmp = image->comps[idx]; if (!(components[idx] = j2k_Component_construct(cmp.w, cmp.h, cmp.prec, cmp.sgnd, cmp.x0, cmp.y0, cmp.dx, cmp.dy, error))) { goto CATCH_ERROR; } } switch(image->color_space) { case OPJ_CLRSPC_SRGB: imageType = J2K_TYPE_RGB; break; case OPJ_CLRSPC_GRAY: imageType = J2K_TYPE_MONO; break; default: imageType = J2K_TYPE_UNKNOWN; } if (!(impl->container = j2k_Container_construct(image->x1 - image->x0, image->y1 - image->y0, image->numcomps, components, tileWidth, tileHeight, imageType, error))) { goto CATCH_ERROR; } } goto CLEANUP; CATCH_ERROR: { rc = NRT_FAILURE; } CLEANUP: { opj_destroy_cstr_info(&codeStreamInfo); OpenJPEG_cleanup(&stream, &codec, &image); } 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; }
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; }
OpenJPEGReader_readRegion(J2K_USER_DATA *data, nrt_Uint32 x0, nrt_Uint32 y0, nrt_Uint32 x1, nrt_Uint32 y1, nrt_Uint8 **buf, nrt_Error *error) { OpenJPEGReaderImpl *impl = (OpenJPEGReaderImpl*) data; opj_stream_t *stream = NULL; opj_image_t *image = NULL; opj_codec_t *codec = NULL; nrt_Uint64 bufSize; nrt_Uint64 offset = 0; nrt_Uint32 componentBytes, nComponents; if (!OpenJPEG_setup(impl, &stream, &codec, error)) { goto CATCH_ERROR; } /* unfortunately, we need to read the header every time ... */ if (!opj_read_header(stream, codec, &image)) { /*nrt_Error_init(error, "Error reading header", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } if (x1 == 0) x1 = j2k_Container_getWidth(impl->container, error); if (y1 == 0) y1 = j2k_Container_getHeight(impl->container, error); /* only decode what we want */ if (!opj_set_decode_area(codec, image, x0, y0, x1, y1)) { /*nrt_Error_init(error, "Error decoding area", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } nComponents = j2k_Container_getNumComponents(impl->container, error); componentBytes = (j2k_Container_getPrecision(impl->container, error) - 1) / 8 + 1; bufSize = (nrt_Uint64)(x1 - x0) * (y1 - y0) * componentBytes * nComponents; if (buf && !*buf) { *buf = (nrt_Uint8*)J2K_MALLOC(bufSize); if (!*buf) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } } { int keepGoing; OPJ_UINT32 tileIndex, reqSize; OPJ_INT32 tileX0, tileY0, tileX1, tileY1; do { if (!opj_read_tile_header(codec, stream, &tileIndex, &reqSize, &tileX0, &tileY0, &tileX1, &tileY1, &nComponents, &keepGoing)) { /*nrt_Error_init(error, "Error reading tile header", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } if (keepGoing) { if (!opj_decode_tile_data(codec, tileIndex, (*buf + offset), reqSize, stream)) { /*nrt_Error_init(error, "Error decoding tile", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } offset += reqSize; } } while (keepGoing); } goto CLEANUP; CATCH_ERROR: { bufSize = 0; } CLEANUP: { OpenJPEG_cleanup(&stream, &codec, &image); } return bufSize; }
OpenJPEGReader_readTile(J2K_USER_DATA *data, nrt_Uint32 tileX, nrt_Uint32 tileY, nrt_Uint8 **buf, nrt_Error *error) { OpenJPEGReaderImpl *impl = (OpenJPEGReaderImpl*) data; opj_stream_t *stream = NULL; opj_image_t *image = NULL; opj_codec_t *codec = NULL; nrt_Uint32 bufSize; const OPJ_UINT32 tileWidth = j2k_Container_getTileWidth(impl->container, error); const OPJ_UINT32 tileHeight = j2k_Container_getTileHeight(impl->container, error); size_t numBitsPerPixel = 0; size_t numBytesPerPixel = 0; nrt_Uint64 fullBufSize = 0; if (!OpenJPEG_setup(impl, &stream, &codec, error)) { goto CATCH_ERROR; } /* unfortunately, we need to read the header every time ... */ if (!opj_read_header(stream, codec, &image)) { /*nrt_Error_init(error, "Error reading header", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } /* only decode what we want */ if (!opj_set_decode_area(codec, image, tileWidth * tileX, tileHeight * tileY, tileWidth * (tileX + 1), tileHeight * (tileY + 1))) { /*nrt_Error_init(error, "Error decoding area", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } { int keepGoing; OPJ_UINT32 tileIndex, nComponents; OPJ_INT32 tileX0, tileY0, tileX1, tileY1; if (!opj_read_tile_header(codec, stream, &tileIndex, &bufSize, &tileX0, &tileY0, &tileX1, &tileY1, &nComponents, &keepGoing)) { /*nrt_Error_init(error, "Error reading tile header", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } if (keepGoing) { /* TODO: The way blockIO->cntl->blockOffsetInc is currently * implemented in ImageIO.c corresponds with how a * non-compressed partial block would be laid out in a * NITF - the actual extra columns would have been read. * Not sure how the J2K data is laid out on disk but * OpenJPEG is hiding this from us if the extra columns are * present there. So whenever we get a partial tile that * isn't at the full width, we need to add in these extra * columns of 0's ourselves. Potentially we could update * ImageIO.c to not require this instead. Note that we * don't need to pad out the extra rows for a partial block * that isn't the full height because ImageIO will never try * to memcpy these in - we only need to get the stride to * work out correctly. */ const OPJ_UINT32 thisTileWidth = tileX1 - tileX0; const OPJ_UINT32 thisTileHeight = tileY1 - tileY0; 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; } numBitsPerPixel = j2k_Container_getPrecision(impl->container, error); numBytesPerPixel = (numBitsPerPixel / 8) + (numBitsPerPixel % 8 != 0); fullBufSize = tileWidth * thisTileHeight * numBytesPerPixel; } else { fullBufSize = bufSize; } if (buf && !*buf) { *buf = (nrt_Uint8*)J2K_MALLOC(fullBufSize); if (!*buf) { nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, NRT_ERR_MEMORY); goto CATCH_ERROR; } } if (!opj_decode_tile_data(codec, tileIndex, *buf, bufSize, stream)) { /*nrt_Error_init(error, "Error decoding tile", NRT_CTXT, NRT_ERR_UNK);*/ goto CATCH_ERROR; } if (thisTileWidth < tileWidth) { /* We have a tile that isn't as wide as it "should" be * Need to add in the extra columns ourselves. By marching * through the rows backwards, we can do this in place. */ const size_t srcStride = thisTileWidth * numBytesPerPixel; const size_t destStride = tileWidth * numBytesPerPixel; const size_t numLeftoverBytes = destStride - srcStride; OPJ_UINT32 lastRow = thisTileHeight - 1; size_t srcOffset = lastRow * srcStride; size_t destOffset = lastRow * destStride; OPJ_UINT32 ii; nrt_Uint8* bufPtr = *buf; for (ii = 0; ii < thisTileHeight; ++ii, srcOffset -= srcStride, destOffset -= destStride) { nrt_Uint8* const dest = bufPtr + destOffset; memmove(dest, bufPtr + srcOffset, srcStride); memset(dest + srcStride, 0, numLeftoverBytes); } } } } goto CLEANUP; CATCH_ERROR: { fullBufSize = 0; } CLEANUP: { OpenJPEG_cleanup(&stream, &codec, &image); } return fullBufSize; }