From: Miloslav Číž #movingToGitLab Date: Sat, 13 Jul 2019 13:45:53 +0000 (+0200) Subject: [DO NOT MERGE YET] Add More Pixel Art Upscaling Algorithms (#1152) X-Git-Tag: 7.0.8-54~42 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8142136e48842b0ce21de8da644ff829ccf35b9a;p=imagemagick [DO NOT MERGE YET] Add More Pixel Art Upscaling Algorithms (#1152) * Start extending magnify function * Fix pixel alignment * Reimplement scale2x in a new function * Create IntensitiesEqual function * Implement eagle algorithm * Retrieve magnification alg from -define * Make use of -define with -magnify * Make use of magnification variable * alg -> method * Implement scale3x * Implement eagle3x * Compare pixels by channels * Implement eagle3xb * Implement EPX * Start implementing hq2x * Fix hq2x * Fix formatting * Add const qualifiers * epx -> epbx * Implement XBR * Fix XBR * Implement fish * Fix indexed color bug * Refactor --- diff --git a/MagickCore/resize.c b/MagickCore/resize.c index 2c1d4bf99..6a56f6edc 100644 --- a/MagickCore/resize.c +++ b/MagickCore/resize.c @@ -2035,6 +2035,662 @@ MagickExport Image *LiquidRescaleImage(const Image *image, } #endif + +void CopyPixel(const Quantum *src,size_t src_off,Quantum *dst,size_t dst_off, + size_t channels) +{ + register ssize_t + i; + + for (i = 0; i < channels; i++) + dst[channels*dst_off+i] = src[src_off*channels+i]; +} + +/* + Helper function for pixel art scaling. +*/ +void MixPixels(const Quantum *src,size_t *src_offs,size_t src_offs_size, + Quantum *dst,size_t dst_off,size_t channels) +{ + int sum; + + register ssize_t + i, j; + + for (i = 0; i < channels; i++) + { + sum = 0; + + for (j = 0; j < src_offs_size; j++) + sum += src[src_offs[j] * channels + i]; + + dst[channels*dst_off+i] = sum / src_offs_size; + } +} + +void Mix2Pixels(const Quantum *src,size_t src_off1,size_t src_off2,Quantum *dst, + size_t dst_off,size_t channels) +{ + MixPixels(src,(size_t[2]){src_off1,src_off2},2,dst,dst_off,channels); +} + +int PixelsEqual(const Quantum *src1,size_t off1,const Quantum *src2,size_t off2, + size_t channels) +{ + register ssize_t + i; + + off1 *= channels; + off2 *= channels; + + for (i=0; i=0; i--) + { + result += order * pattern[i]; + order *= 2; + } + return result; +} + +void Hq2X(const Image *src_image,const Quantum *neighbourhood,Quantum *result, + size_t channels) +{ + static const unsigned int Hq2XTable[] = + { + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13, + 4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14, + 4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14, + 4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14 + }; + + const int pattern1[] = + { + !PixelsEqual(neighbourhood,4,neighbourhood,8,channels), + !PixelsEqual(neighbourhood,4,neighbourhood,7,channels), + !PixelsEqual(neighbourhood,4,neighbourhood,6,channels), + !PixelsEqual(neighbourhood,4,neighbourhood,5,channels), + !PixelsEqual(neighbourhood,4,neighbourhood,3,channels), + !PixelsEqual(neighbourhood,4,neighbourhood,2,channels), + !PixelsEqual(neighbourhood,4,neighbourhood,1,channels), + !PixelsEqual(neighbourhood,4,neighbourhood,0,channels) + }; + +#define Rotated(p) p[2], p[4], p[7], p[1], p[6], p[0], p[3], p[5] + const int pattern2[] = { Rotated(pattern1) }; + const int pattern3[] = { Rotated(pattern2) }; + const int pattern4[] = { Rotated(pattern3) }; +#undef Rotated + + Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern1)],neighbourhood,result,0, + channels,4,0,1,3,5,7); + + Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern2)],neighbourhood,result,1, + channels,4,2,5,1,7,3); + + Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern3)],neighbourhood,result,3, + channels,4,8,7,5,3,1); + + Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern4)],neighbourhood,result,2, + channels,4,6,3,7,1,5); +} + +void Fish2X(const Image *src_image,const Quantum *neighbourhood, + Quantum *result,size_t channels) +{ + MagickFloatType intensities[9]; + + for (unsigned int i = 0; i < 9; i++) + intensities[i] = GetPixelIntensity(src_image,neighbourhood + i*channels); + + CopyPixel(neighbourhood,0,result,0,channels); + CopyPixel(neighbourhood,intensities[0] > intensities[1] ? 0 : 1,result,1, + channels); + CopyPixel(neighbourhood,intensities[0] > intensities[3] ? 0 : 3,result,2, + channels); + + const int ae = PixelsEqual(neighbourhood,0,neighbourhood,4,channels); + const int bd = PixelsEqual(neighbourhood,1,neighbourhood,3,channels); + const int ab = PixelsEqual(neighbourhood,0,neighbourhood,1,channels); + const int de = PixelsEqual(neighbourhood,3,neighbourhood,4,channels); + const int ad = PixelsEqual(neighbourhood,0,neighbourhood,3,channels); + const int be = PixelsEqual(neighbourhood,1,neighbourhood,4,channels); + +#define Corner(A,B,C,D)\ +if (intensities[B] > intensities[A])\ + MixPixels(neighbourhood,(size_t[3]){B,C,D},3,result,3,channels);\ +else\ + MixPixels(neighbourhood,(size_t[3]){A,B,C},3,result,3,channels); + +#define Line(A,B,C,D)\ +if (intensities[C] > intensities[A])\ + Mix2Pixels(neighbourhood,C,D,result,3,channels);\ +else\ + Mix2Pixels(neighbourhood,A,B,result,3,channels);\ + + if (ae && bd && ab) + CopyPixel(neighbourhood,0,result,3,channels); + else if (ad && de && !ab) + Corner(1,0,4,3) + else if (be && de && !ab) + Corner(0,1,3,4) + else if (ad && ab && !be) + Corner(4,3,1,0) + else if (ab && be && !ad) + Corner(3,0,4,1) + else if (ae && (!bd || intensities[1] > intensities[0])) + Mix2Pixels(neighbourhood,0,4,result,3,channels); + else if (bd && (!ae || intensities[0] > intensities[1])) + Mix2Pixels(neighbourhood,1,3,result,3,channels); + else if (ab) + Line(0,1,3,4) + else if (de) + Line(3,4,0,1) + else if (ad) + Line(0,3,1,4) + else if (be) + Line(1,4,0,3) + else + MixPixels(neighbourhood,(size_t[4]){0,1,3,4},4,result,3,channels); + +#undef Corner +#undef Line +} + +void Xbr2X(const Image *src_image,const Quantum *neighbourhood, + Quantum *result,size_t channels) +{ +#define WeightVar(M,N) const int w_##M##_##N = \ + PixelsEqual(neighbourhood,M,neighbourhood,N,channels) ? 0 : 1; + WeightVar(12,11) + WeightVar(12,7) + WeightVar(12,13) + WeightVar(12,17) + WeightVar(12,16) + WeightVar(12,8) + WeightVar(6,10) + WeightVar(6,2) + WeightVar(11,7) + WeightVar(11,17) + WeightVar(11,5) + WeightVar(7,13) + WeightVar(7,1) + WeightVar(12,6) + WeightVar(12,18) + WeightVar(8,14) + WeightVar(8,2) + WeightVar(13,17) + WeightVar(13,9) + WeightVar(7,3) + WeightVar(16,10) + WeightVar(16,22) + WeightVar(17,21) + WeightVar(11,15) + WeightVar(18,14) + WeightVar(18,22) + WeightVar(17,23) + WeightVar(17,19) +#undef WeightVar + + if ( + w_12_16 + w_12_8 + w_6_10 + w_6_2 + (4 * w_11_7) < + w_11_17 + w_11_5 + w_7_13 + w_7_1 + (4 * w_12_6) + ) + Mix2Pixels(neighbourhood,w_12_11 <= w_12_7 ? 11 : 7,12,result,0,channels); + else + CopyPixel(neighbourhood,12,result,0,channels); + + if ( + w_12_18 + w_12_6 + w_8_14 + w_8_2 + (4 * w_7_13) < + w_13_17 + w_13_9 + w_11_7 + w_7_3 + (4 * w_12_8) + ) + Mix2Pixels(neighbourhood,w_12_7 <= w_12_13 ? 7 : 13,12,result,1,channels); + else + CopyPixel(neighbourhood,12,result,1,channels); + + if ( + w_12_6 + w_12_18 + w_16_10 + w_16_22 + (4 * w_11_17) < + w_11_7 + w_11_15 + w_13_17 + w_17_21 + (4 * w_12_16) + ) + Mix2Pixels(neighbourhood,w_12_11 <= w_12_17 ? 11 : 17,12,result,2,channels); + else + CopyPixel(neighbourhood,12,result,2,channels); + + if ( + w_12_8 + w_12_16 + w_18_14 + w_18_22 + (4 * w_13_17) < + w_11_17 + w_17_23 + w_17_19 + w_7_13 + (4 * w_12_18) + ) + Mix2Pixels(neighbourhood,w_12_13 <= w_12_17 ? 13 : 17,12,result,3,channels); + else + CopyPixel(neighbourhood,12,result,3,channels); +} + +void Scale2X(const Image *src_image,const Quantum *neighbourhood, + Quantum *result,size_t channels) +{ + if (PixelsEqual(neighbourhood,1,neighbourhood,7,channels) || + PixelsEqual(neighbourhood,3,neighbourhood,5,channels)) + { + for (unsigned char i=0; i<4; i++) + CopyPixel(neighbourhood,4,result,i,channels); + } + else + { + if (PixelsEqual(neighbourhood,1,neighbourhood,3,channels)) + CopyPixel(neighbourhood,3,result,0,channels); + else + CopyPixel(neighbourhood,4,result,0,channels); + + if (PixelsEqual(neighbourhood,1,neighbourhood,5,channels)) + CopyPixel(neighbourhood,5,result,1,channels); + else + CopyPixel(neighbourhood,4,result,1,channels); + + if (PixelsEqual(neighbourhood,3,neighbourhood,7,channels)) + CopyPixel(neighbourhood,3,result,2,channels); + else + CopyPixel(neighbourhood,4,result,2,channels); + + if (PixelsEqual(neighbourhood,5,neighbourhood,7,channels)) + CopyPixel(neighbourhood,5,result,3,channels); + else + CopyPixel(neighbourhood,4,result,3,channels); + } +} + +void Epbx2X(const Image *src_image,const Quantum *neighbourhood,Quantum *result, + size_t channels) +{ +#define HelperCond(a,b,c,d,e,f,g) (\ + PixelsEqual(neighbourhood,a,neighbourhood,b,channels) && (\ + PixelsEqual(neighbourhood,c,neighbourhood,d,channels) ||\ + PixelsEqual(neighbourhood,c,neighbourhood,e,channels) ||\ + PixelsEqual(neighbourhood,a,neighbourhood,f,channels) ||\ + PixelsEqual(neighbourhood,b,neighbourhood,g,channels)\ + )\ + ) + + for (unsigned char i=0; i<4; i++) + CopyPixel(neighbourhood,4,result,i,channels); + + if ( + !PixelsEqual(neighbourhood,3,neighbourhood,5,channels) && + !PixelsEqual(neighbourhood,1,neighbourhood,7,channels) && + ( + PixelsEqual(neighbourhood,4,neighbourhood,3,channels) || + PixelsEqual(neighbourhood,4,neighbourhood,7,channels) || + PixelsEqual(neighbourhood,4,neighbourhood,5,channels) || + PixelsEqual(neighbourhood,4,neighbourhood,1,channels) || + ( + ( + !PixelsEqual(neighbourhood,0,neighbourhood,8,channels) || + PixelsEqual(neighbourhood,4,neighbourhood,6,channels) || + PixelsEqual(neighbourhood,3,neighbourhood,2,channels) + ) && + ( + !PixelsEqual(neighbourhood,6,neighbourhood,2,channels) || + PixelsEqual(neighbourhood,4,neighbourhood,0,channels) || + PixelsEqual(neighbourhood,4,neighbourhood,8,channels) + ) + ) + ) + ) + { + if (HelperCond(1,3,4,0,8,2,6)) + Mix2Pixels(neighbourhood,1,3,result,0,channels); + + if (HelperCond(5,1,4,2,6,8,0)) + Mix2Pixels(neighbourhood,5,1,result,1,channels); + + if (HelperCond(3,7,4,6,2,0,8)) + Mix2Pixels(neighbourhood,3,7,result,2,channels); + + if (HelperCond(7,5,4,8,0,6,2)) + Mix2Pixels(neighbourhood,7,5,result,3,channels); + } + +#undef HelperCond +} + +void Eagle3X(const Image *src_image,const Quantum *neighbourhood, + Quantum *result,size_t channels) +{ + const int corner_tl = + PixelsEqual(neighbourhood,0,neighbourhood,1,channels) && + PixelsEqual(neighbourhood,0,neighbourhood,3,channels); + + const int corner_tr = + PixelsEqual(neighbourhood,1,neighbourhood,2,channels) && + PixelsEqual(neighbourhood,2,neighbourhood,5,channels); + + const int corner_bl = + PixelsEqual(neighbourhood,3,neighbourhood,6,channels) && + PixelsEqual(neighbourhood,6,neighbourhood,7,channels); + + const int corner_br = + PixelsEqual(neighbourhood,5,neighbourhood,7,channels) && + PixelsEqual(neighbourhood,7,neighbourhood,8,channels); + + CopyPixel(neighbourhood,corner_tl ? 0 : 4,result,0,channels); + + if (corner_tl && corner_tr) + Mix2Pixels(neighbourhood,0,2,result,1,channels); + else + CopyPixel(neighbourhood,4,result,1,channels); + + CopyPixel(neighbourhood,corner_tr ? 1 : 4,result,2,channels); + + if (corner_tl && corner_bl) + Mix2Pixels(neighbourhood,0,6,result,3,channels); + else + CopyPixel(neighbourhood,4,result,3,channels); + + CopyPixel(neighbourhood,4,result,4,channels); + + if (corner_tr && corner_br) + Mix2Pixels(neighbourhood,2,8,result,5,channels); + else + CopyPixel(neighbourhood,4,result,5,channels); + + CopyPixel(neighbourhood,corner_bl ? 3 : 4,result,6,channels); + + if (corner_bl && corner_br) + Mix2Pixels(neighbourhood,6,8,result,7,channels); + else + CopyPixel(neighbourhood,4,result,7,channels); + + CopyPixel(neighbourhood,corner_br ? 5 : 4,result,8,channels); +} + +void Eagle3XB(const Image *src_image,const Quantum *neighbourhood, + Quantum *result,size_t channels) +{ + const int corner_tl = + PixelsEqual(neighbourhood,0,neighbourhood,1,channels) && + PixelsEqual(neighbourhood,0,neighbourhood,3,channels); + + const int corner_tr = + PixelsEqual(neighbourhood,1,neighbourhood,2,channels) && + PixelsEqual(neighbourhood,2,neighbourhood,5,channels); + + const int corner_bl = + PixelsEqual(neighbourhood,3,neighbourhood,6,channels) && + PixelsEqual(neighbourhood,6,neighbourhood,7,channels); + + const int corner_br = + PixelsEqual(neighbourhood,5,neighbourhood,7,channels) && + PixelsEqual(neighbourhood,7,neighbourhood,8,channels); + + CopyPixel(neighbourhood,corner_tl ? 0 : 4,result,0,channels); + CopyPixel(neighbourhood,4,result,1,channels); + CopyPixel(neighbourhood,corner_tr ? 1 : 4,result,2,channels); + CopyPixel(neighbourhood,4,result,3,channels); + CopyPixel(neighbourhood,4,result,4,channels); + CopyPixel(neighbourhood,4,result,5,channels); + CopyPixel(neighbourhood,corner_bl ? 3 : 4,result,6,channels); + CopyPixel(neighbourhood,4,result,7,channels); + CopyPixel(neighbourhood,corner_br ? 5 : 4,result,8,channels); +} + +void Scale3X(const Image *src_image,const Quantum *neighbourhood, + Quantum *result,size_t channels) +{ + if (!PixelsEqual(neighbourhood,1,neighbourhood,7,channels) && + !PixelsEqual(neighbourhood,3,neighbourhood,5,channels)) + { + if (PixelsEqual(neighbourhood,3,neighbourhood,1,channels)) + CopyPixel(neighbourhood,3,result,0,channels); + else + CopyPixel(neighbourhood,4,result,0,channels); + + if ( + ( + PixelsEqual(neighbourhood,3,neighbourhood,1,channels) && + !PixelsEqual(neighbourhood,4,neighbourhood,2,channels) + ) || + ( + PixelsEqual(neighbourhood,5,neighbourhood,1,channels) && + !PixelsEqual(neighbourhood,4,neighbourhood,0,channels) + ) + ) + CopyPixel(neighbourhood,1,result,1,channels); + else + CopyPixel(neighbourhood,4,result,1,channels); + + if (PixelsEqual(neighbourhood,5,neighbourhood,1,channels)) + CopyPixel(neighbourhood,5,result,2,channels); + else + CopyPixel(neighbourhood,4,result,2,channels); + + if ( + ( + PixelsEqual(neighbourhood,3,neighbourhood,1,channels) && + !PixelsEqual(neighbourhood,4,neighbourhood,6,channels) + ) || + ( + PixelsEqual(neighbourhood,3,neighbourhood,7,channels) && + !PixelsEqual(neighbourhood,4,neighbourhood,0,channels) + ) + ) + CopyPixel(neighbourhood,3,result,3,channels); + else + CopyPixel(neighbourhood,4,result,3,channels); + + CopyPixel(neighbourhood,4,result,4,channels); + + if ( + ( + PixelsEqual(neighbourhood,5,neighbourhood,1,channels) && + !PixelsEqual(neighbourhood,4,neighbourhood,8,channels) + ) || + ( + PixelsEqual(neighbourhood,5,neighbourhood,7,channels) && + !PixelsEqual(neighbourhood,4,neighbourhood,2,channels) + ) + ) + CopyPixel(neighbourhood,5,result,5,channels); + else + CopyPixel(neighbourhood,4,result,5,channels); + + if (PixelsEqual(neighbourhood,3,neighbourhood,7,channels)) + CopyPixel(neighbourhood,3,result,6,channels); + else + CopyPixel(neighbourhood,4,result,6,channels); + + if ( + ( + PixelsEqual(neighbourhood,3,neighbourhood,7,channels) && + !PixelsEqual(neighbourhood,4,neighbourhood,8,channels) + ) || + ( + PixelsEqual(neighbourhood,5,neighbourhood,7,channels) && + !PixelsEqual(neighbourhood,4,neighbourhood,6,channels) + ) + ) + CopyPixel(neighbourhood,7,result,7,channels); + else + CopyPixel(neighbourhood,4,result,7,channels); + + if (PixelsEqual(neighbourhood,5,neighbourhood,7,channels)) + CopyPixel(neighbourhood,5,result,8,channels); + else + CopyPixel(neighbourhood,4,result,8,channels); + } + else + { + for (unsigned char i=0; i<9; i++) + CopyPixel(neighbourhood,4,result,i,channels); + } +} + /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % @@ -2069,6 +2725,7 @@ MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception) *magnify_view; Image + *source_image, *magnify_image; MagickBooleanType @@ -2080,6 +2737,90 @@ MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception) ssize_t y; + const char * + algorithm; + + unsigned char + magnification, + neighbourhood; + + void + (*alg_function)(const Image *,const Quantum *,Quantum *,size_t) = NULL; + + algorithm = GetImageOption(image->image_info,"magnify:method"); + + if (algorithm == (char *) NULL) + algorithm = "scale2x"; + + if (LocaleCompare(algorithm,"scale2x") == 0 || + LocaleCompare(algorithm,"epx") == 0 || + LocaleCompare(algorithm,"scale2") == 0) + { + alg_function = Scale2X; + magnification = 2; + neighbourhood = 3; + } + else if (LocaleCompare(algorithm,"eagle2x") == 0 || + LocaleCompare(algorithm,"eagle2") == 0 || + LocaleCompare(algorithm,"eagle") == 0) + { + alg_function = Eagle2X; + magnification = 2; + neighbourhood = 3; + } + else if (LocaleCompare(algorithm,"scale3x") == 0 || + LocaleCompare(algorithm,"scale3") == 0) + { + alg_function = Scale3X; + magnification = 3; + neighbourhood = 3; + } + else if (LocaleCompare(algorithm,"eagle3x") == 0 || + LocaleCompare(algorithm,"eagle3") == 0) + { + alg_function = Eagle3X; + magnification = 3; + neighbourhood = 3; + } + else if (LocaleCompare(algorithm,"eagle3xb") == 0 || + LocaleCompare(algorithm,"eagle3b") == 0) + { + alg_function = Eagle3XB; + magnification = 3; + neighbourhood = 3; + } + else if (LocaleCompare(algorithm,"epbx") == 0 || + LocaleCompare(algorithm,"epbx2") == 0 || + LocaleCompare(algorithm,"epbx2x") == 0) + { + alg_function = Epbx2X; + magnification = 2; + neighbourhood = 3; + } + else if (LocaleCompare(algorithm,"hqx") == 0 || + LocaleCompare(algorithm,"hq2x") == 0) + { + alg_function = Hq2X; + magnification = 2; + neighbourhood = 3; + } + else if (LocaleCompare(algorithm,"fish") == 0 || + LocaleCompare(algorithm,"fish2") == 0 || + LocaleCompare(algorithm,"fish2x") == 0) + { + alg_function = Fish2X; + magnification = 2; + neighbourhood = 3; + } + else if (LocaleCompare(algorithm,"xbr") == 0 || + LocaleCompare(algorithm,"xbr2") == 0 || + LocaleCompare(algorithm,"xbr2x") == 0) + { + alg_function = Xbr2X; + magnification = 2; + neighbourhood = 5; + } + /* Initialize magnified image attributes. */ @@ -2089,22 +2830,40 @@ MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); - magnify_image=CloneImage(image,2*image->columns,2*image->rows,MagickTrue, + + /* + Make a working copy of the source image and convert it to RGB colorspace. + */ + source_image=CloneImage(image,image->columns,image->rows,MagickTrue, exception); + OffsetInfo offset; + offset.x = 0; + offset.y = 0; + RectangleInfo rectangle; + rectangle.x = 0; + rectangle.y = 0; + rectangle.width = image->columns; + rectangle.height = image->rows; + CopyImagePixels(source_image,image,&rectangle,&offset,exception); + SetImageColorspace(source_image,RGBColorspace,exception); + + magnify_image=CloneImage(source_image,magnification*source_image->columns, + magnification*source_image->rows,MagickTrue,exception); if (magnify_image == (Image *) NULL) return((Image *) NULL); + /* - Magnify image. + Magnify the image. */ status=MagickTrue; progress=0; - image_view=AcquireVirtualCacheView(image,exception); + image_view=AcquireVirtualCacheView(source_image,exception); magnify_view=AcquireAuthenticCacheView(magnify_image,exception); #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(progress,status) \ - magick_number_threads(image,magnify_image,image->rows,1) + magick_number_threads(source_image,magnify_image,source_image->rows,1) #endif - for (y=0; y < (ssize_t) image->rows; y++) + for (y=0; y < (ssize_t) source_image->rows; y++) { register Quantum *magick_restrict q; @@ -2112,98 +2871,50 @@ MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception) register ssize_t x; + Quantum r[128]; // to hold result pixels + if (status == MagickFalse) continue; - q=QueueCacheViewAuthenticPixels(magnify_view,0,2*y,magnify_image->columns,2, - exception); + q=QueueCacheViewAuthenticPixels(magnify_view,0,magnification*y, + magnify_image->columns,magnification,exception); if (q == (Quantum *) NULL) { status=MagickFalse; continue; } + /* Magnify this row of pixels. */ - for (x=0; x < (ssize_t) image->columns; x++) + for (x=0; x < (ssize_t) source_image->columns; x++) { - MagickRealType - intensity[9]; - register const Quantum *magick_restrict p; - register Quantum - *magick_restrict r; + ssize_t + channels; register ssize_t - i; + i, j; - size_t - channels; + p=GetCacheViewVirtualPixels(image_view,x-neighbourhood/2, + y-neighbourhood/2,neighbourhood,neighbourhood,exception); + + channels = GetPixelChannels(source_image); - p=GetCacheViewVirtualPixels(image_view,x-1,y-1,3,3,exception); - if (p == (const Quantum *) NULL) - { - status=MagickFalse; - continue; - } - channels=GetPixelChannels(image); - for (i=0; i < 9; i++) - intensity[i]=GetPixelIntensity(image,p+i*channels); - r=q; - if ((fabs(intensity[1]-intensity[7]) < MagickEpsilon) || - (fabs(intensity[3]-intensity[5]) < MagickEpsilon)) - { - /* - Clone center pixel. - */ - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[4*channels+i]; - r+=GetPixelChannels(magnify_image); - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[4*channels+i]; - r+=GetPixelChannels(magnify_image)*(magnify_image->columns-1); - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[4*channels+i]; - r+=GetPixelChannels(magnify_image); - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[4*channels+i]; - } - else - { - /* - Selectively clone pixel. - */ - if (fabs(intensity[1]-intensity[3]) < MagickEpsilon) - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[3*channels+i]; - else - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[4*channels+i]; - r+=GetPixelChannels(magnify_image); - if (fabs(intensity[1]-intensity[5]) < MagickEpsilon) - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[5*channels+i]; - else - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[4*channels+i]; - r+=GetPixelChannels(magnify_image)*(magnify_image->columns-1); - if (fabs(intensity[3]-intensity[7]) < MagickEpsilon) - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[3*channels+i]; - else - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[4*channels+i]; - r+=GetPixelChannels(magnify_image); - if (fabs(intensity[5]-intensity[7]) < MagickEpsilon) - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[5*channels+i]; - else - for (i=0; i < (ssize_t) channels; i++) - r[i]=p[4*channels+i]; - } - q+=2*GetPixelChannels(magnify_image); + alg_function(source_image,p,r,channels); + + /* + Copy the result pixels into the final image. + */ + for (j=0; j < magnification; j++) + for (i=0; i < (ssize_t) channels*magnification; i++) + q[j*channels*magnify_image->columns+i]= + r[j*magnification*channels+i]; + + q+=magnification*GetPixelChannels(magnify_image); } + if (SyncCacheViewAuthenticPixels(magnify_view,exception) == MagickFalse) status=MagickFalse; if (image->progress_monitor != (MagickProgressMonitor) NULL) @@ -2223,7 +2934,10 @@ MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception) magnify_view=DestroyCacheView(magnify_view); image_view=DestroyCacheView(image_view); if (status == MagickFalse) + { magnify_image=DestroyImage(magnify_image); + source_image=DestroyImage(source_image); + } return(magnify_image); }