#pragma once //------------------------------------------------------------------------------------------------------------------------------- #include "Types.h" #include "Reader.h" #include "J2k.h" namespace Jpeg2000 { //------------------------------------------------------------------------------------------------------------------------------- // Вспомогательные функции //------------------------------------------------------------------------------------------------------------------------------- static bool Jp2_ReadBoxHeader(PCommon pCodecInfo, CReader *pStream, Jp2Box *pBox) { pBox->nInitPos = pStream->Tell(); pBox->nLength = pStream->Read(4); pBox->nType = pStream->Read(4); if (pBox->nLength == 1) { if (pStream->Read(4) != 0) { Event_Message(EVT_ERROR, "Cannot handle box sizes higher than 2^32\n"); return false; } pBox->nLength = pStream->Read(4); if (pBox->nLength == 0) pBox->nLength = pStream->GetLeftSize() + 12; } else if (pBox->nLength == 0) { int nBytesLeft = pStream->GetLeftSize(); if (0 >= nBytesLeft) return false; pBox->nLength = nBytesLeft + 8; } return true; } static void Jp2_WriteURL(CReader *pStream, char *sIndexFile) { Jp2Box oBox; oBox.nInitPos = pStream->Tell(); pStream->Skip(4); pStream->Write(JP2_URL, 4); // DBTL pStream->Write(0, 1); // VERS pStream->Write(0, 3); // FLAG if (sIndexFile) { for (unsigned int nIndex = 0; nIndex < strlen(sIndexFile); nIndex++) { pStream->Write(sIndexFile[nIndex], 1); } } oBox.nLength = pStream->Tell() - oBox.nInitPos; pStream->Seek(oBox.nInitPos); pStream->Write(oBox.nLength, 4); // L pStream->Seek(oBox.nInitPos + oBox.nLength); } static bool Jp2_ReadIHDR(Jp2Stream *pJp2, CReader *pStream) { Jp2Box box; PCommon pInfo = pJp2->pCodecInfo; Jp2_ReadBoxHeader(pInfo, pStream, &box); if (JP2_IHDR != box.nType) { Event_Message(EVT_ERROR, "Expected IHDR Marker\n"); return false; } pJp2->nHeight = pStream->Read(4); // HEIGHT pJp2->nWidth = pStream->Read(4); // WIDTH pJp2->nComponentsCount = pStream->Read(2); // NC pJp2->pComponents = (Jp2Component*)Malloc(pJp2->nComponentsCount * sizeof(Jp2Component)); if (!pJp2->pComponents) { Event_Message(EVT_ERROR, "Not enough memory\n"); pJp2->pCodecInfo->nErrorCode = JP2_ERROR_NOT_ENOUGH_MEMORY; return false; } pJp2->nBPC = pStream->Read(1); // BPC pJp2->nCompressionType = pStream->Read(1); // C pJp2->nColorSpaceUnk = pStream->Read(1); // UnkC pJp2->nIPR = pStream->Read(1); // IPR if (pStream->Tell() - box.nInitPos != box.nLength) { Event_Message(EVT_ERROR, "Error with IHDR Box\n"); return false; } return true; } static void Jp2_WriteIHDR(Jp2Stream *pJp2, CReader *pStream) { Jp2Box box; box.nInitPos = pStream->Tell(); pStream->Skip(4); pStream->Write(JP2_IHDR, 4); // IHDR pStream->Write(pJp2->nHeight, 4); // HEIGHT pStream->Write(pJp2->nWidth, 4); // WIDTH pStream->Write(pJp2->nComponentsCount, 2); // NC pStream->Write(pJp2->nBPC, 1); // BPC pStream->Write(pJp2->nCompressionType, 1); // C ( это значение всегда равно 7 ) pStream->Write(pJp2->nColorSpaceUnk, 1); // UnkC, colorspace unknown pStream->Write(pJp2->nIPR, 1); // IPR box.nLength = pStream->Tell() - box.nInitPos; pStream->Seek(box.nInitPos); pStream->Write(box.nLength, 4); // L pStream->Seek(box.nInitPos + box.nLength); } static void Jp2_WriteBPCC(Jp2Stream *pJp2, CReader *pStream) { Jp2Box oBox; oBox.nInitPos = pStream->Tell(); pStream->Skip(4); pStream->Write(JP2_BPCC, 4); // BPCC for (unsigned int nIndex = 0; nIndex < pJp2->nComponentsCount; nIndex++) { pStream->Write(pJp2->pComponents[nIndex].nBPCC, 1); } oBox.nLength = pStream->Tell() - oBox.nInitPos; pStream->Seek(oBox.nInitPos); pStream->Write(oBox.nLength, 4); // L pStream->Seek(oBox.nInitPos + oBox.nLength); } static bool Jp2_ReadBPCC(Jp2Stream *pJp2, CReader *pStream) { PCommon pCodecInfo = pJp2->pCodecInfo; Jp2Box oBox; Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); if (JP2_BPCC != oBox.nType) { Event_Message(EVT_ERROR, "Expected BPCC Marker\n"); return false; } for (unsigned int nIndex = 0; nIndex < pJp2->nComponentsCount; nIndex++) { pJp2->pComponents[nIndex].nBPCC = pStream->Read(1); } if (pStream->Tell() - oBox.nInitPos != oBox.nLength) { Event_Message(EVT_ERROR, "Error with BPCC Box\n"); return false; } return true; } static void Jp2_WriteCOLR(Jp2Stream *pJp2, CReader *pStream) { Jp2Box oBox; oBox.nInitPos = pStream->Tell(); pStream->Skip(4); pStream->Write(JP2_COLR, 4); // COLR //pStream->Write( 1/*pJp2->nMethod*/, 1 ); // METH //pStream->Write( 0/*pJp2->nPrec*/, 1 ); // PREC //pStream->Write( 0/*pJp2->nApprox*/, 1 ); // APPROX //pStream->Write( 16/*pJp2->nEnumCS*/, 4); // EnumCS pStream->Write(pJp2->nMethod, 1); // METH pStream->Write(pJp2->nPrec, 1); // PREC pStream->Write(pJp2->nApprox, 1); // APPROX if (1 == pJp2->nMethod) { pStream->Write(pJp2->nEnumCS, 4); // EnumCS } else { pStream->Write(0, 1); // PROFILE } oBox.nLength = pStream->Tell() - oBox.nInitPos; pStream->Seek(oBox.nInitPos); pStream->Write(oBox.nLength, 4); // L pStream->Seek(oBox.nInitPos + oBox.nLength); } static bool Jp2_ReadCOLR(Jp2Stream *pJp2, CReader *pStream) { if (pJp2->oColor.has_colr) { // TODO: print error "A conforming JP2 reader shall ignore all Colour Specification boxes after the first, so we ignore this one.\n" return true; } PCommon pCodecInfo = pJp2->pCodecInfo; Jp2Box oBox; Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); do { if (JP2_COLR != oBox.nType) { if (oBox.nType == JP2_JP2C) { Event_Message(EVT_ERROR, "Expected JP2H Marker\n"); return false; } pStream->Skip(oBox.nLength - 8); Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); } } while (JP2_COLR != oBox.nType); pJp2->nMethod = pStream->Read(1); // METH pJp2->nPrec = pStream->Read(1); // PREC pJp2->nApprox = pStream->Read(1); // APPROX if (pJp2->nMethod == 1) { pJp2->nEnumCS = pStream->Read(4); // EnumCS pJp2->oColor.has_colr = true; } else if (pJp2->nMethod == 2) { /* ICC profile */ int it_icc_value = 0; int icc_len = oBox.nLength - 3; pJp2->oColor.icc_profile_len = icc_len; pJp2->oColor.icc_profile_buf = (BYTE*)Malloc(icc_len); if (!pJp2->oColor.icc_profile_buf) { pJp2->oColor.icc_profile_len = 0; return false; } for (it_icc_value = 0; it_icc_value < icc_len; ++it_icc_value) { pJp2->oColor.icc_profile_buf[it_icc_value] = (BYTE)pStream->Read(1); /* icc values */ } pJp2->oColor.has_colr = true; } else// if (jp2->meth > 2) { /* ISO/IEC 15444-1:2004 (E), Table I.9 ­ Legal METH values: conforming JP2 reader shall ignore the entire Colour Specification box.*/ //"COLR BOX meth value is not a regular value (%d), so we will ignore the entire Colour Specification box. \n", jp2->meth); // Пропускаем PROFILE int nSkipLen = oBox.nInitPos + oBox.nLength - pStream->Tell(); if (nSkipLen < 0) { Event_Message(EVT_ERROR, "Error with JP2H box size\n"); return false; } pStream->Skip(oBox.nInitPos + oBox.nLength - pStream->Tell()); } if (pStream->Tell() - oBox.nInitPos != oBox.nLength) { Event_Message(EVT_ERROR, "Error with BPCC Box\n"); return false; } return true; } static void Jp2_WriteCDEF(Jp2Stream *pJp2, CReader *pStream) { } static bool Jp2_ReadCDEF(Jp2Stream *pJp2, CReader *pStream) { Jp2Box oBox; PCommon pCodecInfo = pJp2->pCodecInfo; Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); do { if (JP2_CDEF != oBox.nType) { if (0 == oBox.nType) return false; pStream->Skip(oBox.nLength - 8); Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); } } while (JP2_CDEF != oBox.nType); int nComponentsCount = pStream->Read(2); for (int nIndex = 0; nIndex < nComponentsCount; nIndex++) { int nCompNum = pStream->Read(2); int nType = pStream->Read(2); int nAsoc = pStream->Read(2); } return true; } static void Jp2_WriteJP2H(Jp2Stream *pJp2, CReader *pStream) { Jp2Box oBox; oBox.nInitPos = pStream->Tell(); pStream->Skip(4); pStream->Write(JP2_JP2H, 4); // JP2H Jp2_WriteIHDR(pJp2, pStream); if (pJp2->nBPC == 255) { Jp2_WriteBPCC(pJp2, pStream); } Jp2_WriteCOLR(pJp2, pStream); oBox.nLength = pStream->Tell() - oBox.nInitPos; pStream->Seek(oBox.nInitPos); pStream->Write(oBox.nLength, 4); // L pStream->Seek(oBox.nInitPos + oBox.nLength); } static bool Jp2_ReadJP2H(Jp2Stream *pJp2, CReader *pStream) { Jp2Box oBox; PCommon pCodecInfo = pJp2->pCodecInfo; Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); do { if (JP2_JP2H != oBox.nType) { if (oBox.nType == JP2_JP2C) { Event_Message(EVT_ERROR, "Expected JP2H Marker\n"); return false; } pStream->Skip(oBox.nLength - 8); if (!Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox)) return false; } } while (JP2_JP2H != oBox.nType); if (!Jp2_ReadIHDR(pJp2, pStream)) return false; int nCurPos = pStream->Tell(); Jp2_ReadCDEF(pJp2, pStream); pStream->Seek(nCurPos); if (pJp2->nBPC == 255) { if (!Jp2_ReadBPCC(pJp2, pStream)) return false; } nCurPos = pStream->Tell(); if (!Jp2_ReadCOLR(pJp2, pStream)) { // return false; // По спецификации данный Box является необходимым, но мы все-таки // попробуем прочитать изображение со стандартными параметрами. pStream->Seek(nCurPos); pJp2->nApprox = 0; pJp2->nPrec = 0; pJp2->nMethod = 1; pJp2->nEnumCS = 16; // sRGB } int nSkipLen = oBox.nInitPos + oBox.nLength - pStream->Tell(); if (nSkipLen < 0) { Event_Message(EVT_ERROR, "Error with JP2H Box\n"); return false; } pStream->Skip(oBox.nInitPos + oBox.nLength - pStream->Tell()); return true; } static void Jp2_WriteFTYP(Jp2Stream *pJp2, CReader *pStream) { Jp2Box oBox; oBox.nInitPos = pStream->Tell(); pStream->Skip(4); pStream->Write(JP2_FTYP, 4); // FTYP pStream->Write(pJp2->nBrand, 4); // BR pStream->Write(pJp2->nMinVersion, 4); // MinV for (unsigned int nIndex = 0; nIndex < pJp2->nCompListLength; nIndex++) { pStream->Write(pJp2->pCompList[nIndex], 4); // CL } oBox.nLength = pStream->Tell() - oBox.nInitPos; pStream->Seek(oBox.nInitPos); pStream->Write(oBox.nLength, 4); // L pStream->Seek(oBox.nInitPos + oBox.nLength); } static bool Jp2_ReadFTYP(Jp2Stream *pJp2, CReader *pStream) { PCommon pCodecInfo = pJp2->pCodecInfo; Jp2Box oBox; Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); if (JP2_FTYP != oBox.nType) { Event_Message(EVT_ERROR, "Expected FTYP Marker\n"); return false; } pJp2->nBrand = pStream->Read(4); // BR pJp2->nMinVersion = pStream->Read(4); // MinV pJp2->nCompListLength = (oBox.nLength - 16) / 4; pJp2->pCompList = (unsigned int *)Malloc(pJp2->nCompListLength * sizeof(unsigned int)); if (!pJp2->pCompList) { pJp2->pCodecInfo->nErrorCode = JP2_ERROR_NOT_ENOUGH_MEMORY; return false; } pStream->Read((BYTE*)pJp2->pCompList, pJp2->nCompListLength * sizeof(unsigned int)); //for ( unsigned int nIndex = 0; nIndex < (int)pJp2->nCompListLength; nIndex++ ) //{ // pJp2->pCompList[nIndex] = pStream->Read( 4 ); // CLi //} if (pStream->Tell() - oBox.nInitPos != oBox.nLength) { Event_Message(EVT_ERROR, "Error with FTYP Box\n"); return false; } return true; } static int Jp2_WriteJP2C(Jp2Stream *pJp2, CReader *pStream, Image *pImage, char *sIndex) { J2kCodestream *pJ2k = pJp2->pJ2k; Jp2Box oBox; oBox.nInitPos = pStream->Tell(); pStream->Skip(4); pStream->Write(JP2_JP2C, 4); // JP2C // J2K unsigned int nJ2kCodestreamOffset = pStream->Tell(); if (!J2k_Encode(pJ2k, pStream, pImage, sIndex)) { Event_Message(EVT_ERROR, "Failed to encode image\n"); return 0; } unsigned int nJ2kCodestreamLength = pStream->Tell() - nJ2kCodestreamOffset; pJp2->nJ2kCodestreamOffset = nJ2kCodestreamOffset; pJp2->nJ2kCodestreamLength = nJ2kCodestreamLength; oBox.nLength = 8 + pJp2->nJ2kCodestreamLength; pStream->Seek(oBox.nInitPos); pStream->Write(oBox.nLength, 4); // L pStream->Seek(oBox.nInitPos + oBox.nLength); return oBox.nLength; } static bool Jp2_ReadJP2C(Jp2Stream *pJp2, CReader *pStream, unsigned int *pnJ2kCodestreamLength, unsigned int *pnJ2kCodestreamOffset) { Jp2Box oBox; PCommon pCodecInfo = pJp2->pCodecInfo; Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); do { if (JP2_JP2C != oBox.nType) { pStream->Skip(oBox.nLength - 8); Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); } } while (JP2_JP2C != oBox.nType); *pnJ2kCodestreamOffset = pStream->Tell(); *pnJ2kCodestreamLength = oBox.nLength - 8; return true; } static void Jp2_WriteJP(CReader *pStream) { Jp2Box oBox; oBox.nInitPos = pStream->Tell(); pStream->Skip(4); pStream->Write(JP2_JP, 4); // JP2 signature pStream->Write(0x0d0a870a, 4); oBox.nLength = pStream->Tell() - oBox.nInitPos; pStream->Seek(oBox.nInitPos); pStream->Write(oBox.nLength, 4); // L pStream->Seek(oBox.nInitPos + oBox.nLength); } static bool Jp2_ReadJP(Jp2Stream *pJp2, CReader *pStream) { Jp2Box oBox; PCommon pCodecInfo = pJp2->pCodecInfo; Jp2_ReadBoxHeader(pCodecInfo, pStream, &oBox); if (JP2_JP != oBox.nType) { Event_Message(EVT_ERROR, "Expected JP Marker\n"); return false; } if (0x0d0a870a != pStream->Read(4)) { Event_Message(EVT_ERROR, "Error with JP Marker\n"); return false; } if (pStream->Tell() - oBox.nInitPos != oBox.nLength) { Event_Message(EVT_ERROR, "Error with JP Box size\n"); return false; } return true; } static bool Jp2_ReadStruct(Jp2Stream *pJp2, CReader *pStream) { if (!Jp2_ReadJP(pJp2, pStream)) return false; if (!Jp2_ReadFTYP(pJp2, pStream)) return false; if (!Jp2_ReadJP2H(pJp2, pStream)) return false; if (!Jp2_ReadJP2C(pJp2, pStream, &pJp2->nJ2kCodestreamLength, &pJp2->nJ2kCodestreamOffset)) return false; return true; } //------------------------------------------------------------------------------------------------------------------------------- // Декодирование потока Jp2 //------------------------------------------------------------------------------------------------------------------------------- void Jp2_DestroyDecompress(Jp2Stream *pJp2) { if (pJp2) { J2k_DestroyDecompress(pJp2->pJ2k); Free(pJp2->oColor.icc_profile_buf); Free(pJp2->pComponents); Free(pJp2->pCompList); Free(pJp2); } } Jp2Stream* Jp2_CreateDecompress(PCommon pCodecInfo) { Jp2Stream *pJp2 = (Jp2Stream*)Malloc(sizeof(Jp2Stream)); if (pJp2) { pJp2->pCodecInfo = pCodecInfo; pJp2->pJ2k = J2k_CreateDecompress(pCodecInfo); if (NULL == pJp2->pJ2k) { Jp2_DestroyDecompress(pJp2); return NULL; } } else { pCodecInfo->nErrorCode = JP2_ERROR_NOT_ENOUGH_MEMORY; } return pJp2; } void Jp2_SetupDecoder(Jp2Stream *pJp2, DecoderParams *pParameters) { J2k_SetupDecoder(pJp2->pJ2k, pParameters); } Image* Jp2_Decode(Jp2Stream *pJp2, CReader *pStream) { if (!pJp2 || !pStream) { return NULL; } PCommon pCodecInfo = pJp2->pCodecInfo; // Декодируем JP2 if (!Jp2_ReadStruct(pJp2, pStream)) { Event_Message(EVT_ERROR, "Failed to decode jp2 structure\n"); return NULL; } // Декодируем J2K Image *pImage = J2k_Decode(pJp2->pJ2k, pStream); if (!pImage) { Event_Message(EVT_ERROR, "Failed to decode J2K image\n"); } ////приведение цветовой схемы..???? return pImage; } //------------------------------------------------------------------------------------------------------------------------------- // Кодирование в поток Jp2 //------------------------------------------------------------------------------------------------------------------------------- void Jp2_DestroyCompress(Jp2Stream *pJp2) { if (pJp2) { J2k_DestroyCompress(pJp2->pJ2k); Free(pJp2->pComponents); Free(pJp2->pCompList); Free(pJp2); } } Jp2Stream* Jp2_CreateCompress(PCommon pCodecInfo) { Jp2Stream *pJp2 = (Jp2Stream*)Malloc(sizeof(Jp2Stream)); if (pJp2) { pJp2->pCodecInfo = pCodecInfo; pJp2->pJ2k = J2k_CreateCompress(pCodecInfo); if (NULL == pJp2->pJ2k) { Jp2_DestroyCompress(pJp2); return NULL; } } else { pCodecInfo->nErrorCode = JP2_ERROR_NOT_ENOUGH_MEMORY; } return pJp2; } void Jp2_SetupEncoder(Jp2Stream *pJp2, EncoderParams *pParameters, Image *pImage) { if (!pJp2 || !pParameters || !pImage) return; if (pImage->nCsiz < 1 || pImage->nCsiz > 16384) { Event_Message(EVT_ERROR, "Invalid number of components specified while setting up JP2 encoder\n"); return; } J2k_SetupEncoder(pJp2->pJ2k, pParameters, pImage); if (JP2_ERROR_NO_ERROR != pJp2->pCodecInfo->nErrorCode) return; // Profile box pJp2->nBrand = JP2_JP2; // BR pJp2->nMinVersion = 0; // MinV pJp2->nCompListLength = 1; pJp2->pCompList = (unsigned int*)Malloc(pJp2->nCompListLength * sizeof(unsigned int)); if (!pJp2->pCompList) { pJp2->pCodecInfo->nErrorCode = JP2_ERROR_NOT_ENOUGH_MEMORY; return; } pJp2->pCompList[0] = JP2_JP2; // CL0: JP2 // Image Header box pJp2->nComponentsCount = pImage->nCsiz; // NC pJp2->pComponents = (Jp2Component*)Malloc(pJp2->nComponentsCount * sizeof(Jp2Component)); if (!pJp2->pComponents) { Free(pJp2->pCompList); pJp2->pCodecInfo->nErrorCode = JP2_ERROR_NOT_ENOUGH_MEMORY; return; } pJp2->nHeight = pImage->nYsiz - pImage->nYOsiz; // HEIGHT pJp2->nWidth = pImage->nXsiz - pImage->nXOsiz; // WIDTH int nDepth0 = pImage->pComponents[0].nPrecision - 1; int nSign = pImage->pComponents[0].nSigned; pJp2->nBPC = nDepth0 + (nSign << 7); // BPC for (int nIndex = 1; nIndex < pImage->nCsiz; nIndex++) { int depth = pImage->pComponents[nIndex].nPrecision - 1; nSign = pImage->pComponents[nIndex].nSigned; if (nDepth0 != depth) pJp2->nBPC = 255; } pJp2->nCompressionType = 7; // C (всегда 7) pJp2->nColorSpaceUnk = 0; // UnkC pJp2->nIPR = 0; // IRP // BitsPerComponent box for (int nIndex = 0; nIndex < pImage->nCsiz; nIndex++) { pJp2->pComponents[nIndex].nBPCC = pImage->pComponents[nIndex].nPrecision - 1 + (pImage->pComponents[nIndex].nSigned << 7); } // Colour Specification box if ((pImage->nCsiz == 1 || pImage->nCsiz == 3) && (pJp2->nBPC != 255)) { pJp2->nMethod = 1; // METH: Enumerated colourspace } else { pJp2->nMethod = 2; // METH: Restricted ICC profile } if (pJp2->nMethod == 1) { if (pImage->eColorSpace == 1) pJp2->nEnumCS = 16; // sRGB ( 61966–2–1 ) else if (pImage->eColorSpace == 2) pJp2->nEnumCS = 17; // Greyscale else if (pImage->eColorSpace == 3) pJp2->nEnumCS = 18; // YUV } else { pJp2->nEnumCS = 0; // PROFILE } pJp2->nPrec = 0; // PREC pJp2->nApprox = 0; // APPROX } bool Jp2_Encode(Jp2Stream *pJp2, CReader *pStream, Image *pImage, char *sIndex) { // JP2 encoding // JPEG 2000 Signature box Jp2_WriteJP(pStream); // File Type box Jp2_WriteFTYP(pJp2, pStream); // JP2 Header box Jp2_WriteJP2H(pJp2, pStream); // J2K encoding if (!Jp2_WriteJP2C(pJp2, pStream, pImage, sIndex)) { Event_Message(EVT_ERROR, "Failed to encode image\n"); return false; } return true; } }