/* * This file is part of RawTherapee. * * Copyright (c) 2004-2010 Gabor Horvath * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RawTherapee is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ #include #include "colortemp.h" #include "image16.h" #include "imagefloat.h" #include "image8.h" #include "rt_math.h" namespace { void getScanline8(const uint16_t *red, const uint16_t *green, const uint16_t *blue, int width, unsigned char* buffer) { for (int i = 0, ix = 0; i < width; i++) { buffer[ix++] = rtengine::uint16ToUint8Rounded(red[i]); buffer[ix++] = rtengine::uint16ToUint8Rounded(green[i]); buffer[ix++] = rtengine::uint16ToUint8Rounded(blue[i]); } } void getScanline16(const uint16_t *red, const uint16_t *green, const uint16_t *blue, int width, unsigned short* buffer) { for (int i = 0, ix = 0; i < width; i++) { buffer[ix++] = red[i]; buffer[ix++] = green[i]; buffer[ix++] = blue[i]; } } } using namespace rtengine; Image16::Image16() { } Image16::Image16(int w, int h) { allocate(w, h); } Image16::~Image16() { } void Image16::getScanline(int row, unsigned char* buffer, int bps, bool isFloat) const { if (data == nullptr) { return; } if (bps == 16) { getScanline16(r(row), g(row), b(row), width, (unsigned short*)buffer); } else if (bps == 8) { getScanline8(r(row), g(row), b(row), width, buffer); } } void Image16::setScanline(int row, const unsigned char* buffer, int bps, unsigned int numSamples) { if (data == nullptr) { return; } switch (sampleFormat) { case (IIOSF_UNSIGNED_CHAR): { int ix = 0; if (numSamples == 1) { for (int i = 0; i < width; ++i) { r(row, i) = g(row, i) = b(row, i) = static_cast(buffer[ix++]) * 257; } } else { for (int i = 0; i < width; ++i) { r(row, i) = static_cast(buffer[ix++]) * 257; g(row, i) = static_cast(buffer[ix++]) * 257; b(row, i) = static_cast(buffer[ix++]) * 257; } } break; } case (IIOSF_UNSIGNED_SHORT): { const unsigned short* sbuffer = (const unsigned short*) buffer; int ix = 0; for (int i = 0; i < width; ++i) { r(row, i) = sbuffer[ix++]; g(row, i) = sbuffer[ix++]; b(row, i) = sbuffer[ix++]; } break; } default: // Other types are ignored, but could be implemented if necessary break; } /* * Not used for now * */ } Image16* Image16::copy() const { Image16* cp = new Image16(width, height); copyData(cp); return cp; } Image16* Image16::copySubRegion (int x, int y, int width, int height) { Image16* cp = NULL; int realWidth = LIM(x + width, 0, this->width) - x; int realHeight = LIM(y + height, 0, this->height) - y; if (realWidth > 0 && realHeight > 0) { cp = new Image16 (realWidth, realHeight); copyData(cp, x, y, realWidth, realHeight); } return cp; } void Image16::getStdImage(const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp) const { // compute channel multipliers float rm = 1.f, gm = 1.f, bm = 1.f; if (ctemp.getTemp() >= 0) { double drm, dgm, dbm; ctemp.getMultipliers(drm, dgm, dbm); rm = drm; gm = dgm; bm = dbm; rm = 1.f / rm; gm = 1.f / gm; bm = 1.f / bm; float mul_lum = 0.299f * rm + 0.587f * gm + 0.114f * bm; rm /= mul_lum; gm /= mul_lum; bm /= mul_lum; } int sx1, sy1, sx2, sy2; transform(pp, tran, sx1, sy1, sx2, sy2); int imwidth = image->getWidth(); // Destination image int imheight = image->getHeight(); // Destination image if (((tran & TR_ROT) == TR_R90) || ((tran & TR_ROT) == TR_R270)) { int swap = imwidth; imwidth = imheight; imheight = swap; } int maxx = width; // Source image int maxy = height; // Source image int mtran = tran & TR_ROT; int skip = pp.getSkip(); //if ((sx1 + skip*imwidth)>maxx) imwidth -- ; // we have a boundary condition that can cause errors // improve speed by integrating the area division into the multipliers // switched to using ints for the red/green/blue channel buffer. // Incidentally this improves accuracy too. float area = skip * skip; float rm2 = rm; float gm2 = gm; float bm2 = bm; rm /= area; gm /= area; bm /= area; #ifdef _OPENMP #pragma omp parallel { #endif AlignedBuffer abR(imwidth); AlignedBuffer abG(imwidth); AlignedBuffer abB(imwidth); float *lineR = abR.data; float *lineG = abG.data; float *lineB = abB.data; #ifdef _OPENMP #pragma omp for #endif // Iterating all the rows of the destination image for (int iy = 0; iy < imheight; iy++) { if (skip == 1) { // special case (speedup for 1:1 scale) // i: source image, first line of the current destination row int src_y = sy1 + iy; // overflow security check, not sure that it's necessary if (src_y >= maxy) { continue; } for (int dst_x = 0, src_x = sx1; dst_x < imwidth; dst_x++, src_x++) { // overflow security check, not sure that it's necessary if (src_x >= maxx) { continue; } lineR[dst_x] = CLIP(rm2 * r(src_y, src_x)); lineG[dst_x] = CLIP(gm2 * g(src_y, src_x)); lineB[dst_x] = CLIP(bm2 * b(src_y, src_x)); } } else { // source image, first line of the current destination row int src_y = sy1 + skip * iy; if (src_y >= maxy) { continue; } for (int dst_x = 0, src_x = sx1; dst_x < imwidth; dst_x++, src_x += skip) { if (src_x >= maxx) { continue; } int src_sub_width = MIN(maxx - src_x, skip); int src_sub_height = MIN(maxy - src_y, skip); float rtot, gtot, btot; // RGB accumulators rtot = gtot = btot = 0.; for (int src_sub_y = 0; src_sub_y < src_sub_height; src_sub_y++) for (int src_sub_x = 0; src_sub_x < src_sub_width; src_sub_x++) { rtot += r(src_y + src_sub_y, src_x + src_sub_x); gtot += g(src_y + src_sub_y, src_x + src_sub_x); btot += b(src_y + src_sub_y, src_x + src_sub_x); } // convert back to gamma and clip if (src_sub_width == skip && src_sub_height == skip) { // Common case where the sub-region is complete lineR[dst_x] = CLIP(rm * rtot); lineG[dst_x] = CLIP(gm * gtot); lineB[dst_x] = CLIP(bm * btot); } else { // computing a special factor for this incomplete sub-region float larea = src_sub_width * src_sub_height; lineR[dst_x] = CLIP(rm2 * rtot / larea); lineG[dst_x] = CLIP(gm2 * gtot / larea); lineB[dst_x] = CLIP(bm2 * btot / larea); } } } if (mtran == TR_NONE) for (int dst_x = 0, src_x = sx1; dst_x < imwidth; dst_x++, src_x += skip) { image->r(iy, dst_x) = lineR[dst_x]; image->g(iy, dst_x) = lineG[dst_x]; image->b(iy, dst_x) = lineB[dst_x]; } else if (mtran == TR_R180) for (int dst_x = 0; dst_x < imwidth; dst_x++) { image->r(imheight - 1 - iy, imwidth - 1 - dst_x) = lineR[dst_x]; image->g(imheight - 1 - iy, imwidth - 1 - dst_x) = lineG[dst_x]; image->b(imheight - 1 - iy, imwidth - 1 - dst_x) = lineB[dst_x]; } else if (mtran == TR_R90) for (int dst_x = 0, src_x = sx1; dst_x < imwidth; dst_x++, src_x += skip) { image->r(dst_x, imheight - 1 - iy) = lineR[dst_x]; image->g(dst_x, imheight - 1 - iy) = lineG[dst_x]; image->b(dst_x, imheight - 1 - iy) = lineB[dst_x]; } else if (mtran == TR_R270) for (int dst_x = 0, src_x = sx1; dst_x < imwidth; dst_x++, src_x += skip) { image->r(imwidth - 1 - dst_x, iy) = lineR[dst_x]; image->g(imwidth - 1 - dst_x, iy) = lineG[dst_x]; image->b(imwidth - 1 - dst_x, iy) = lineB[dst_x]; } } #ifdef _OPENMP } #endif #undef GCLIP } // Parallelized transformation; create transform with cmsFLAGS_NOCACHE! void Image16::ExecCMSTransform(cmsHTRANSFORM hTransform) { //cmsDoTransform(hTransform, data, data, planestride); // LittleCMS cannot parallelize planar setups -- Hombre: LCMS2.4 can! But it we use this new feature, memory allocation have to be modified too // so build temporary buffers to allow multi processor execution #ifdef _OPENMP #pragma omp parallel #endif { AlignedBuffer buffer(width * 3); #ifdef _OPENMP #pragma omp for schedule(static) #endif for (int y = 0; y < height; y++) { unsigned short *p = buffer.data, *pR = r(y), *pG = g(y), *pB = b(y); for (int x = 0; x < width; x++) { *(p++) = *(pR++); *(p++) = *(pG++); *(p++) = *(pB++); } cmsDoTransform(hTransform, buffer.data, buffer.data, width); p = buffer.data; pR = r(y); pG = g(y); pB = b(y); for (int x = 0; x < width; x++) { *(pR++) = *(p++); *(pG++) = *(p++); *(pB++) = *(p++); } } // End of parallelization } } // // Parallelized transformation; create transform with cmsFLAGS_NOCACHE! // void Image16::ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy) // { // // LittleCMS cannot parallelize planar Lab float images // // so build temporary buffers to allow multi processor execution // #ifdef _OPENMP // #pragma omp parallel // #endif // { // AlignedBuffer bufferLab(width * 3); // AlignedBuffer bufferRGB(width * 3); // #ifdef _OPENMP // #pragma omp for schedule(static) // #endif // for (int y = cy; y < cy + height; y++) // { // unsigned short *pRGB, *pR, *pG, *pB; // float *pLab, *pL, *pa, *pb; // pLab= bufferLab.data; // pL = labImage.L[y] + cx; // pa = labImage.a[y] + cx; // pb = labImage.b[y] + cx; // for (int x = 0; x < width; x++) { // *(pLab++) = *(pL++) / 327.68f; // *(pLab++) = *(pa++) / 327.68f; // *(pLab++) = *(pb++) / 327.68f; // } // cmsDoTransform (hTransform, bufferLab.data, bufferRGB.data, width); // pRGB = bufferRGB.data; // pR = r(y - cy); // pG = g(y - cy); // pB = b(y - cy); // for (int x = 0; x < width; x++) { // *(pR++) = *(pRGB++); // *(pG++) = *(pRGB++); // *(pB++) = *(pRGB++); // } // } // End of parallelization // } // }