#include "epicsv4Grayscale.h" epics::pvData::StructureConstPtr makeVariantArrayStruc() { FieldConstPtrArray vaFields; StringArray vaNames; vaFields.push_back(getFieldCreate()->createScalar(epics::pvData::pvInt)); vaNames.push_back("dataType"); vaFields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvInt)); vaNames.push_back("value"); epics::pvData::StructureConstPtr varrayStruc = getFieldCreate()->createStructure("uri:ev4:nt/2012/pwd:NTVariantArray", vaNames, vaFields); return varrayStruc; } epics::pvData::StructureConstPtr makeImageStruc() { static epics::pvData::StructureConstPtr imageStruc; if (imageStruc == NULL) { FieldConstPtrArray fields; StringArray names; // Array part fields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvByte)); names.push_back("value"); fields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvInt)); names.push_back("dim"); // Image part fields.push_back(getFieldCreate()->createScalar(epics::pvData::pvInt)); names.push_back("colorMode"); fields.push_back(getFieldCreate()->createScalar(epics::pvData::pvInt)); names.push_back("bayerPattern"); fields.push_back(getFieldCreate()->createScalar(epics::pvData::pvString)); names.push_back("fourcc"); fields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvInt)); names.push_back("offset"); fields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvInt)); names.push_back("binning"); fields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvInt)); names.push_back("reverse"); fields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvInt)); names.push_back("fullDim"); // Metadata part fields.push_back(getFieldCreate()->createScalar(epics::pvData::pvInt)); names.push_back("uniqueId"); fields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvInt)); names.push_back("attributeSourceTypes"); fields.push_back(getFieldCreate()->createScalarArray(epics::pvData::pvString)); names.push_back("attributeSources"); fields.push_back(getFieldCreate()->createStructureArray(makeVariantArrayStruc())); names.push_back("attributes"); imageStruc = getFieldCreate()->createStructure("uri:ev4:nt/2012/pwd:NTImage", names, fields); } return imageStruc; } void setImageArrayValues(PVStructure::shared_pointer const & imagePV) { String id = imagePV->getStructure()->getID(); PVByteArrayPtr pvField = static_pointer_cast(imagePV->getSubField("value")); size_t dataSize = epicsv4_raw_size; pvField->setCapacity(dataSize); const int8_t *data = epicsv4_raw; pvField->put(0, dataSize, data, 0); PVIntArrayPtr dimField = static_pointer_cast( imagePV->getScalarArrayField(String("dim"), pvInt)); dimField->setCapacity(2); const int32_t *dim = epicsv4_raw_dim; dimField->put(0, 2, dim, 0); } void setImageImageValues(PVStructure::shared_pointer const & imagePV) { PVIntArrayPtr offsetField = static_pointer_cast( imagePV->getScalarArrayField(String("offset"), pvInt)); offsetField->setCapacity(2); int32_t offsets[] = { 0, 0 }; offsetField->put(0, 2, offsets, 0); PVIntArrayPtr binningField = static_pointer_cast( imagePV->getScalarArrayField(String("binning"), pvInt)); binningField->setCapacity(2); int32_t binnings[] = { 1, 1 }; binningField->put(0, 2, binnings, 0); PVIntArrayPtr reverseField = static_pointer_cast( imagePV->getScalarArrayField(String("reverse"), pvInt)); reverseField->setCapacity(2); int32_t reverses[] = { 0, 0 }; reverseField->put(0, 2, reverses, 0); PVIntArrayPtr fullDimField = static_pointer_cast( imagePV->getScalarArrayField(String("fullDim"), pvInt)); fullDimField->setCapacity(2); const int32_t *fullDim = epicsv4_raw_dim; fullDimField->put(0, 2, fullDim, 0); } void setImageUniqueId(PVStructure::shared_pointer const & imagePV) { PVIntPtr uniqueIdField = imagePV->getIntField(String("uniqueId")); uniqueIdField->put(0); } void setImageMetadataValues(PVStructure::shared_pointer const & imagePV) { setImageUniqueId(imagePV); } void initImage(PVStructure::shared_pointer const & imagePV) { setImageArrayValues(imagePV); setImageImageValues(imagePV); setImageMetadataValues(imagePV); } void rotateImage(PVStructure::shared_pointer const & imagePV, float deg) { PVScalarArrayPtr value = static_pointer_cast(imagePV->getSubField("value")); PVIntArrayPtr dim = static_pointer_cast(imagePV->getScalarArrayField("dim", pvInt)); // dim[] = { rows, columns } int32 rows, cols; size_t dims = dim->getLength(); IntArrayData data; dim->get(0, dims, data); cols = data.data[0]; rows = data.data[1]; PVByteArrayPtr array = static_pointer_cast(value); ByteArrayData dimg; size_t imgSize = array->getLength(); array->get(0, imgSize, dimg); double fi = 3.141592653589793238462 * deg / 180.0; double cosFi = 16.0 * cos(fi); double sinFi = 16.0 * sin(fi); int32 cx = cols/2; int32 cy = rows/2; int32 colsm2 = cols-2; int32 rowsm2 = rows-2; int8_t* img = &dimg.data[0]; for (int32 y = 0; y < rows; y++) { int8_t* imgline = img + y*cols; int32 dcy = y - cy; for (int32 x = 0; x < cols; x++) { int32 dcx = x - cx; int32 tnx = static_cast(cosFi*dcx + sinFi*dcy); int32 tny = static_cast(-sinFi*dcx + cosFi*dcy); int32 nx = (tnx >> 4) + cx; int32 ny = (tny >> 4) + cy; if (nx < 0 || ny < 0 || nx > colsm2 || ny > rowsm2) { imgline[x] = 0; } else { const int8_t* srcline = epicsv4_raw + ny*cols; int32 xf = tnx & 0x0F; int32 yf = tny & 0x0F; int32 v00 = (16 - xf) * (16 - yf) * (srcline[nx] + 128); int32 v10 = xf * (16 - yf) * (srcline[nx + 1] + 128); int32 v01 = (16 - xf) * yf * (srcline[cols + nx] + 128); int32 v11 = xf * yf * (srcline[cols + nx + 1] + 128); uint8_t val = static_cast((v00 + v01 + v10 + v11 + 128) / 256); imgline[x] = static_cast(val) - 128; } } } array->put(0, imgSize, img, 0); PVIntPtr uniqueIdField = imagePV->getIntField(String("uniqueId")); uniqueIdField->put(uniqueIdField->get()+1); } void rotateAMColorFastLow(uint32 *datad, int32 w, int32 h, int32 wpld, uint32 *datas, int32 wpls, float angle, uint32 colorval); void rotateRGBImage(PVStructure::shared_pointer const & imagePV, float deg) { PVScalarArrayPtr value = static_pointer_cast(imagePV->getSubField("value")); PVIntArrayPtr dim = static_pointer_cast(imagePV->getScalarArrayField("dim", pvInt)); // dim[] = { rows, columns } int32 rows, cols; size_t dims = dim->getLength(); IntArrayData data; dim->get(0, dims, data); cols = data.data[0]; rows = data.data[1]; PVIntArrayPtr array = static_pointer_cast(value); IntArrayData dimg; size_t imgSize = array->getLength(); array->get(0, imgSize, dimg); rotateAMColorFastLow((uint32*)&dimg.data[0], cols, rows, cols, (uint32*)&dimg.data[0], cols, 3.141592653589793238462 * deg / 180.0, 0x00000000); array->put(0, imgSize, &dimg.data[0], 0); PVIntPtr uniqueIdField = imagePV->getIntField(String("uniqueId")); uniqueIdField->put(uniqueIdField->get()+1); } /*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ #include #include /*------------------------------------------------------------------* * Fast RGB color rotation about center * *------------------------------------------------------------------*/ /*! * rotateAMColorFastLow() * * This is a special simplification of area mapping with division * of each pixel into 16 sub-pixels. The exact coefficients that * should be used are the same as for the 4x linear interpolation * scaling case, and are given there. I tried to approximate these * as weighted coefficients with a maximum sum of 4, which * allows us to do the arithmetic in parallel for the R, G and B * components in a 32 bit pixel. However, there are three reasons * for not doing that: * (1) the loss of accuracy in the parallel implementation * is visually significant * (2) the parallel implementation (described below) is slower * (3) the parallel implementation requires allocation of * a temporary color image * * There are 16 cases for the choice of the subpixel, and * for each, the mapping to the relevant source * pixels is as follows: * * subpixel src pixel weights * -------- ----------------- * 0 sp1 * 1 (3 * sp1 + sp2) / 4 * 2 (sp1 + sp2) / 2 * 3 (sp1 + 3 * sp2) / 4 * 4 (3 * sp1 + sp3) / 4 * 5 (9 * sp1 + 3 * sp2 + 3 * sp3 + sp4) / 16 * 6 (3 * sp1 + 3 * sp2 + sp3 + sp4) / 8 * 7 (3 * sp1 + 9 * sp2 + sp3 + 3 * sp4) / 16 * 8 (sp1 + sp3) / 2 * 9 (3 * sp1 + sp2 + 3 * sp3 + sp4) / 8 * 10 (sp1 + sp2 + sp3 + sp4) / 4 * 11 (sp1 + 3 * sp2 + sp3 + 3 * sp4) / 8 * 12 (sp1 + 3 * sp3) / 4 * 13 (3 * sp1 + sp2 + 9 * sp3 + 3 * sp4) / 16 * 14 (sp1 + sp2 + 3 * sp3 + 3 * sp4) / 8 * 15 (sp1 + 3 * sp2 + 3 * sp3 + 9 * sp4) / 16 * * Another way to visualize this is to consider the area mapping * (or linear interpolation) coefficients for the pixel sp1. * Expressed in fourths, they can be written as asymmetric matrix: * * 4 3 2 1 * 3 2.25 1.5 0.75 * 2 1.5 1 0.5 * 1 0.75 0.5 0.25 * * The coefficients for the three neighboring pixels can be * similarly written. * * This is implemented here, where, for each color component, * we inline its extraction from each participating word, * construct the linear combination, and combine the results * into the destination 32 bit RGB pixel, using the appropriate shifts. * * It is interesting to note that an alternative method, where * we do the arithmetic on the 32 bit pixels directly (after * shifting the components so they won't overflow into each other) * is significantly inferior. Because we have only 8 bits for * internal overflows, which can be distributed as 2, 3, 3, it * is impossible to add these with the correct linear * interpolation coefficients, which require a sum of up to 16. * Rounding off to a sum of 4 causes appreciable visual artifacts * in the rotated image. The code for the inferior method * can be found in prog/rotatefastalt.c, for reference. * * *** Warning: explicit assumption about RGB component ordering *** */ void rotateAMColorFastLow(uint32 *datad, int32 w, int32 h, int32 wpld, uint32 *datas, int32 wpls, float angle, uint32 colorval) { int32 i, j, xcen, ycen, wm2, hm2; int32 xdif, ydif, xpm, ypm, xp, yp, xf, yf; uint32 word1, word2, word3, word4, red, blue, green; uint32 *pword, *lines, *lined; float sina, cosa; xcen = w / 2; wm2 = w - 2; ycen = h / 2; hm2 = h - 2; sina = 4. * sin(angle); cosa = 4. * cos(angle); for (i = 0; i < h; i++) { ydif = ycen - i; lined = datad + i * wpld; for (j = 0; j < w; j++) { xdif = xcen - j; xpm = (int32)(-xdif * cosa - ydif * sina); ypm = (int32)(-ydif * cosa + xdif * sina); xp = xcen + (xpm >> 2); yp = ycen + (ypm >> 2); xf = xpm & 0x03; yf = ypm & 0x03; /* if off the edge, write input grayval */ if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) { *(lined + j) = colorval; continue; } lines = datas + yp * wpls; pword = lines + xp; switch (xf + 4 * yf) { case 0: *(lined + j) = *pword; break; case 1: word1 = *pword; word2 = *(pword + 1); red = 3 * (word1 >> 24) + (word2 >> 24); green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff); blue = 3 * ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff); *(lined + j) = ((red << 22) & 0xff000000) | ((green << 14) & 0x00ff0000) | ((blue << 6) & 0x0000ff00); break; case 2: word1 = *pword; word2 = *(pword + 1); red = (word1 >> 24) + (word2 >> 24); green = ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff); blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff); *(lined + j) = ((red << 23) & 0xff000000) | ((green << 15) & 0x00ff0000) | ((blue << 7) & 0x0000ff00); break; case 3: word1 = *pword; word2 = *(pword + 1); red = (word1 >> 24) + 3 * (word2 >> 24); green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff); blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff); *(lined + j) = ((red << 22) & 0xff000000) | ((green << 14) & 0x00ff0000) | ((blue << 6) & 0x0000ff00); break; case 4: word1 = *pword; word3 = *(pword + wpls); red = 3 * (word1 >> 24) + (word3 >> 24); green = 3 * ((word1 >> 16) & 0xff) + ((word3 >> 16) & 0xff); blue = 3 * ((word1 >> 8) & 0xff) + ((word3 >> 8) & 0xff); *(lined + j) = ((red << 22) & 0xff000000) | ((green << 14) & 0x00ff0000) | ((blue << 6) & 0x0000ff00); break; case 5: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = 9 * (word1 >> 24) + 3 * (word2 >> 24) + 3 * (word3 >> 24) + (word4 >> 24); green = 9 * ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) + 3 * ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff); blue = 9 * ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) + 3 * ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff); *(lined + j) = ((red << 20) & 0xff000000) | ((green << 12) & 0x00ff0000) | ((blue << 4) & 0x0000ff00); break; case 6: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = 3 * (word1 >> 24) + 3 * (word2 >> 24) + (word3 >> 24) + (word4 >> 24); green = 3 * ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) + ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff); blue = 3 * ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) + ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff); *(lined + j) = ((red << 21) & 0xff000000) | ((green << 13) & 0x00ff0000) | ((blue << 5) & 0x0000ff00); break; case 7: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = 3 * (word1 >> 24) + 9 * (word2 >> 24) + (word3 >> 24) + 3 * (word4 >> 24); green = 3 * ((word1 >> 16) & 0xff) + 9 * ((word2 >> 16) & 0xff) + ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff); blue = 3 * ((word1 >> 8) & 0xff) + 9 * ((word2 >> 8) & 0xff) + ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff); *(lined + j) = ((red << 20) & 0xff000000) | ((green << 12) & 0x00ff0000) | ((blue << 4) & 0x0000ff00); break; case 8: word1 = *pword; word3 = *(pword + wpls); red = (word1 >> 24) + (word3 >> 24); green = ((word1 >> 16) & 0xff) + ((word3 >> 16) & 0xff); blue = ((word1 >> 8) & 0xff) + ((word3 >> 8) & 0xff); *(lined + j) = ((red << 23) & 0xff000000) | ((green << 15) & 0x00ff0000) | ((blue << 7) & 0x0000ff00); break; case 9: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = 3 * (word1 >> 24) + (word2 >> 24) + 3 * (word3 >> 24) + (word4 >> 24); green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) + 3 * ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff); blue = 3 * ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) + 3 * ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff); *(lined + j) = ((red << 21) & 0xff000000) | ((green << 13) & 0x00ff0000) | ((blue << 5) & 0x0000ff00); break; case 10: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = (word1 >> 24) + (word2 >> 24) + (word3 >> 24) + (word4 >> 24); green = ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) + ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff); blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) + ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff); *(lined + j) = ((red << 22) & 0xff000000) | ((green << 14) & 0x00ff0000) | ((blue << 6) & 0x0000ff00); break; case 11: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = (word1 >> 24) + 3 * (word2 >> 24) + (word3 >> 24) + 3 * (word4 >> 24); green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) + ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff); blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) + ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff); *(lined + j) = ((red << 21) & 0xff000000) | ((green << 13) & 0x00ff0000) | ((blue << 5) & 0x0000ff00); break; case 12: word1 = *pword; word3 = *(pword + wpls); red = (word1 >> 24) + 3 * (word3 >> 24); green = ((word1 >> 16) & 0xff) + 3 * ((word3 >> 16) & 0xff); blue = ((word1 >> 8) & 0xff) + 3 * ((word3 >> 8) & 0xff); *(lined + j) = ((red << 22) & 0xff000000) | ((green << 14) & 0x00ff0000) | ((blue << 6) & 0x0000ff00); break; case 13: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = 3 * (word1 >> 24) + (word2 >> 24) + 9 * (word3 >> 24) + 3 * (word4 >> 24); green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) + 9 * ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff); blue = 3 *((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) + 9 * ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff); *(lined + j) = ((red << 20) & 0xff000000) | ((green << 12) & 0x00ff0000) | ((blue << 4) & 0x0000ff00); break; case 14: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = (word1 >> 24) + (word2 >> 24) + 3 * (word3 >> 24) + 3 * (word4 >> 24); green = ((word1 >> 16) & 0xff) +((word2 >> 16) & 0xff) + 3 * ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff); blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) + 3 * ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff); *(lined + j) = ((red << 21) & 0xff000000) | ((green << 13) & 0x00ff0000) | ((blue << 5) & 0x0000ff00); break; case 15: word1 = *pword; word2 = *(pword + 1); word3 = *(pword + wpls); word4 = *(pword + wpls + 1); red = (word1 >> 24) + 3 * (word2 >> 24) + 3 * (word3 >> 24) + 9 * (word4 >> 24); green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) + 3 * ((word3 >> 16) & 0xff) + 9 * ((word4 >> 16) & 0xff); blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) + 3 * ((word3 >> 8) & 0xff) + 9 * ((word4 >> 8) & 0xff); *(lined + j) = ((red << 20) & 0xff000000) | ((green << 12) & 0x00ff0000) | ((blue << 4) & 0x0000ff00); break; default: fprintf(stderr, "shouldn't get here\n"); break; } } } return; }