📄 dmtxencode.c
字号:
* @param codewordPosition * @return Randomized value */static unsigned charRandomize255State(unsigned char codewordValue, int codewordPosition){ int pseudoRandom; int tmp; pseudoRandom = ((149 * codewordPosition) % 255) + 1; tmp = codewordValue + pseudoRandom; return (tmp <= 255) ? tmp : tmp - 256;}/** * @brief Write encoded message to image * @param enc * @return void */static voidPrintPattern(DmtxEncode *enc){ int i, j; int symbolRow, symbolCol; int pixelRow, pixelCol; int moduleStatus; double sxy, txy; DmtxRgb rgb; DmtxMatrix3 m1, m2; DmtxVector2 vIn, vOut; txy = enc->marginSize; sxy = 1.0/enc->moduleSize; dmtxMatrix3Translate(m1, -txy, -txy); dmtxMatrix3Scale(m2, sxy, -sxy); dmtxMatrix3Multiply(enc->xfrm, m1, m2); dmtxMatrix3Translate(m1, txy, txy); dmtxMatrix3Scale(m2, enc->moduleSize, enc->moduleSize); dmtxMatrix3Multiply(enc->rxfrm, m2, m1); /* Print raster version of barcode pattern IMPORTANT: DmtxImage is stored with its origin at bottom-right (unlike common image file formats) to preserve "right-handed" 2D space */ memset(enc->image->pxl, 0xff, dmtxImageGetProp(enc->image, DmtxPropArea) * sizeof(DmtxRgb)); for(symbolRow = 0; symbolRow < enc->region.symbolRows; symbolRow++) { for(symbolCol = 0; symbolCol < enc->region.symbolCols; symbolCol++) { vIn.X = symbolCol; vIn.Y = symbolRow; dmtxMatrix3VMultiply(&vOut, &vIn, enc->rxfrm); pixelCol = (int)(vOut.X); pixelRow = (int)(vOut.Y); moduleStatus = dmtxSymbolModuleStatus(enc->message, enc->region.sizeIdx, symbolRow, symbolCol); for(i = pixelRow; i < pixelRow + enc->moduleSize; i++) { for(j = pixelCol; j < pixelCol + enc->moduleSize; j++) { rgb[0] = ((moduleStatus & DMTX_MODULE_ON_RED) != 0x00) ? 0 : 255; rgb[1] = ((moduleStatus & DMTX_MODULE_ON_GREEN) != 0x00) ? 0 : 255; rgb[2] = ((moduleStatus & DMTX_MODULE_ON_BLUE) != 0x00) ? 0 : 255; dmtxImageSetRgb(enc->image, j, i, rgb); } } } }}/** * @brief Initialize encoding channel * @param channel * @param codewords * @param length * @return void */static voidInitChannel(DmtxChannel *channel, unsigned char *codewords, int length){ memset(channel, 0x00, sizeof(DmtxChannel)); channel->encScheme = DmtxSchemeEncodeAscii; channel->invalid = DMTX_CHANNEL_VALID; channel->inputPtr = codewords; channel->inputStop = codewords + length;}/** * @brief Encode message using single encodation scheme * @param buf * @param codewords * @param length * @param scheme * @return Encoded length */static intEncodeSingleScheme(unsigned char *buf, unsigned char *codewords, int length, DmtxSchemeEncode scheme){ int size; DmtxChannel channel; InitChannel(&channel, codewords, length); while(channel.inputPtr < channel.inputStop) { EncodeNextWord(&channel, scheme); /* DumpChannel(&channel); */ if(channel.invalid != 0) { fprintf(stderr, "Character \"%c\" not supported by requested encodation scheme\n\n", *channel.inputPtr); return 0; } } /* DumpChannel(&channel); */ size = channel.encodedLength/12; memcpy(buf, channel.encodedWords, size); return size;}/** * @brief Encode message using best possible encodation (combine schemes) * @param buf * @param codewords * @param length * @return Encoded length of winning channel */static intEncodeAutoBest(unsigned char *buf, unsigned char *codewords, int length){ int targetScheme; int winnerSize; DmtxChannelGroup optimal, best; DmtxChannel *channel, *winner; /* Intialize optimizing channels and encode first codeword from default ASCII */ for(targetScheme = DmtxSchemeEncodeAscii; targetScheme <= DmtxSchemeEncodeBase256; targetScheme++) { channel = &(optimal.channel[targetScheme]); InitChannel(channel, codewords, length); EncodeNextWord(channel, targetScheme); } /* fprintf(stdout,"\nWinners:"); */ /* DumpChannelGroup(&optimal, DmtxSchemeEncodeAscii); */ /* For each remaining word in the input stream, test the efficiency of getting to this encodation scheme for each input character by switching here from each of the other channels (which are always optimally encoded) */ while(optimal.channel[0].inputPtr < optimal.channel[0].inputStop) { /* XXX only tracking first channel */ /* fprintf(stdout,"\n** codeword **\n"); */ for(targetScheme = DmtxSchemeEncodeAscii; targetScheme <= DmtxSchemeEncodeBase256; targetScheme++) { best.channel[targetScheme] = FindBestChannel(optimal, targetScheme); } optimal = best; /* fprintf(stdout, "\nWinners:"); */ /* DumpChannelGroup(&optimal, 0); */ } /* Choose a winner now that all channels are finished */ winner = &(optimal.channel[DmtxSchemeEncodeAscii]); for(targetScheme = DmtxSchemeEncodeAscii + 1; targetScheme <= DmtxSchemeEncodeBase256; targetScheme++) { if(optimal.channel[targetScheme].invalid != 0) continue; if(optimal.channel[targetScheme].encodedLength < winner->encodedLength) winner = &(optimal.channel[targetScheme]); } /* XXX get rid of buf concept and try to do something with channel -> matrix copy instead */ winnerSize = winner->encodedLength/12; memcpy(buf, winner->encodedWords, winnerSize); return winnerSize;}/** * @brief Determine current best channel in encoding process * @param group * @param targetScheme * @return Winning channel */static DmtxChannelFindBestChannel(DmtxChannelGroup group, DmtxSchemeEncode targetScheme){ DmtxSchemeEncode encFrom; DmtxChannel *channel, *winner; winner = NULL; for(encFrom = DmtxSchemeEncodeAscii; encFrom <= DmtxSchemeEncodeBase256; encFrom++) { channel = &(group.channel[encFrom]); /* If from channel doesn't hold valid data because it couldn't represent the previous value then skip it */ if(channel->invalid != 0) continue; /* If channel has already processed all of its input values then it cannot be used as a starting point */ if(channel->inputPtr == channel->inputStop) continue; EncodeNextWord(channel, targetScheme); /* If channel scheme can't represent next word then stop for this channel */ if((channel->invalid & DMTX_CHANNEL_UNSUPPORTED_CHAR) != 0) { winner = channel; break; } /* If channel scheme was unable to unlatch here then skip */ if((channel->invalid & DMTX_CHANNEL_CANNOT_UNLATCH) != 0) continue; if(winner == NULL || channel->currentLength < winner->currentLength) winner = channel; } /* DumpChannelGroup(&group, targetScheme); */ return *winner;}/** * @brief Encode next codeword using requested encodation scheme * @param channel * @param targetScheme * @return void */static voidEncodeNextWord(DmtxChannel *channel, DmtxSchemeEncode targetScheme){ /* Change to new encodation scheme if necessary */ if(channel->encScheme != targetScheme) { ChangeEncScheme(channel, targetScheme, DMTX_UNLATCH_EXPLICIT); if(channel->invalid != 0) return; } assert(channel->encScheme == targetScheme); /* Encode next input value */ switch(channel->encScheme) { case DmtxSchemeEncodeAscii: EncodeAsciiCodeword(channel); break; case DmtxSchemeEncodeC40: EncodeTripletCodeword(channel); break; case DmtxSchemeEncodeText: EncodeTripletCodeword(channel); break; case DmtxSchemeEncodeX12: EncodeTripletCodeword(channel); break; case DmtxSchemeEncodeEdifact: EncodeEdifactCodeword(channel); break; case DmtxSchemeEncodeBase256: EncodeBase256Codeword(channel); break; default: break; }}/** * @brief Encode value using ASCII encodation (standard or extended) * @param channel * @return void */static voidEncodeAsciiCodeword(DmtxChannel *channel){ unsigned char inputValue, prevValue, prevPrevValue; int prevIndex; assert(channel->encScheme == DmtxSchemeEncodeAscii); inputValue = *(channel->inputPtr); /* XXX this is problematic ... We should not be looking backward in the channel to determine what state we're in. Add the necessary logic to fix the current bug (prevprev != 253) but when rewriting encoder later make sure double digit ascii as treated as a forward-encoded condition. i.e., encode ahead of where we currently stand, and not comparable to other channels because currently sitting between byte boundaries (like the triplet-based schemes). Much simpler. */ /* XXX another thought on the future rewrite: if adopting a forward-encoding approach on double digits then the following input situation: digit digit c40a c40b c40c would create: ASCII_double C40_triplet1ab C40_triplet2bc although it might be more efficient in some cases to do digit C40_triplet1(digit a) C40_triplet2(a b) (I can't think of a situation like this, but I can't rule it out either) Unfortunately the forward encoding approach would never allow ascii to unlatch between the ASCII_double input words. One approach that would definitely avoid this is to treat ASCII_dd as a separate channel when using "--best". However, when encoding to single- scheme ascii you would always use the ASCII_dd approach. This function, EncodeAsciiCodeword(), should have another parameter to flag whether or not to compress double digits. When encoding in single scheme ascii, then compress the double digits. If using --best then use both options as separate channels. */ /* 2nd digit char in a row - overwrite first digit word with combined value */ if(isdigit(inputValue) && channel->currentLength >= channel->firstCodeWord + 12) { prevIndex = (channel->currentLength - 12)/12; prevValue = channel->encodedWords[prevIndex] - 1; prevPrevValue = (prevIndex > channel->firstCodeWord/12) ? channel->encodedWords[prevIndex-1] : 0; if(prevPrevValue != 235 && isdigit(prevValue)) { channel->encodedWords[prevIndex] = 10 * (prevValue - '0') + (inputValue - '0') + 130; channel->inputPtr++; return; } } /* Extended ASCII char */ if(inputValue >= 128) { PushInputWord(channel, DMTX_CHAR_ASCII_UPPER_SHIFT); IncrementProgress(channel, 12); inputValue -= 128; } /* Normal ASCII char */ else { inputValue++; } PushInputWord(channel, inputValue); IncrementProgress(channel, 12); channel->inputPtr++;}/** * @brief Encode value using C40, Text, or X12 encodation * @param channel * @return void */static voidEncodeTripletCodeword(DmtxChannel *channel){ int i; int inputCount; int tripletCount; int count; int outputWords[4]; /* biggest: upper shift to non-basic set */ unsigned char buffer[6]; /* biggest: 2 words followed by 4-word upper shift */ DmtxTriplet triplet; unsigned char inputWord; unsigned char *ptr; assert(channel->encScheme == DmtxSchemeEncodeC40 || channel->encScheme == DmtxSchemeEncodeText || channel->encScheme == DmtxSchemeEncodeX12); assert(channel->currentLength <= channel->encodedLength); /* If there are no pre-encoded codewords then generate some */ if(channel->currentLength == channel->encodedLength) { assert(channel->currentLength % 12 == 0); /* Ideally we would only encode one codeword triplet here (the minimum that you can do at a time) but we can't leave the channel with the last encoded word as a shift. The following loop prevents this condition by encoding until we have a clean break or until we reach the end of the input data. */ ptr = channel->inputPtr; tripletCount = 0; for(;;) { /* Fill array with at least 3 values (the minimum necessary to encode a triplet), but possibly up to 6 values due to presence of upper shifts. Note that buffer may already contain values from a previous iteration of the outer loop, and this step "tops off" the buffer to make sure there are at least 3 values. */ while(tripletCount < 3 && ptr < channel->inputStop) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -