From f88d6765a594cf9fb0825b74779f74394a7ccc7a Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Thu, 10 Mar 2011 15:06:27 +0000 Subject: [PATCH] check-point for kLCD16_Format mask support disabled for now in SkPaint.cpp (for further testing) git-svn-id: http://skia.googlecode.com/svn/trunk@917 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkMask.h | 16 +++- include/core/SkScalerContext.h | 17 +++- samplecode/SampleXfermodes.cpp | 47 ++++++---- src/core/SkBlitter_ARGB32.cpp | 76 ++++++++++++++++ src/core/SkPaint.cpp | 22 +++++ src/core/SkScalerContext.cpp | 1 + src/ports/SkFontHost_mac_coretext.cpp | 119 ++++++++++++++++++++------ 7 files changed, 249 insertions(+), 49 deletions(-) diff --git a/include/core/SkMask.h b/include/core/SkMask.h index 58a249341f..ebb870feaf 100644 --- a/include/core/SkMask.h +++ b/include/core/SkMask.h @@ -47,6 +47,7 @@ struct SkMask { kHorizontalLCD_Format, //!< 4 bytes/pixel: a/r/g/b kVerticalLCD_Format, //!< 4 bytes/pixel: a/r/g/b kARGB32_Format, //!< SkPMColor + kLCD16_Format //!< 565 alpha for r/g/b }; enum { @@ -96,6 +97,19 @@ struct SkMask { return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes; } + /** + * Return the address of the specified 16bit mask. In the debug build, + * this asserts that the mask's format is kLCD16_Format, and that (x,y) + * are contained in the mask's fBounds. + */ + uint16_t* getAddrLCD16(int x, int y) const { + SkASSERT(kLCD16_Format == fFormat); + SkASSERT(fBounds.contains(x, y)); + SkASSERT(fImage != NULL); + uint16_t* row = (uint16_t*)(fImage + (y - fBounds.fTop) * fRowBytes); + return row + (x - fBounds.fLeft); + } + /** Return an address into the 32-bit plane of an LCD or VerticalLCD mask for the given position. */ @@ -120,7 +134,7 @@ struct SkMask { static uint8_t* AllocImage(size_t bytes); static void FreeImage(void* image); - + enum CreateMode { kJustComputeBounds_CreateMode, //!< compute bounds and return kJustRenderImage_CreateMode, //!< render into preallocate mask diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h index 317658dbd1..584b86bfb6 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -53,18 +53,27 @@ struct SkGlyph { fMaskFormat = MASK_FORMAT_UNKNOWN; } - unsigned rowBytes() const { - unsigned rb = fWidth; - if (SkMask::kBW_Format == fMaskFormat) { + /** + * Compute the rowbytes for the specified width and mask-format. + */ + static unsigned ComputeRowBytes(unsigned width, SkMask::Format format) { + unsigned rb = width; + if (SkMask::kBW_Format == format) { rb = (rb + 7) >> 3; - } else if (SkMask::kARGB32_Format == fMaskFormat) { + } else if (SkMask::kARGB32_Format == format) { rb <<= 2; + } else if (SkMask::kLCD16_Format == format) { + rb = SkAlign4(rb << 1); } else { rb = SkAlign4(rb); } return rb; } + unsigned rowBytes() const { + return ComputeRowBytes(fWidth, (SkMask::Format)fMaskFormat); + } + bool isJustAdvance() const { return MASK_FORMAT_JUST_ADVANCE == fMaskFormat; } diff --git a/samplecode/SampleXfermodes.cpp b/samplecode/SampleXfermodes.cpp index a85bee2240..b5361e35a6 100644 --- a/samplecode/SampleXfermodes.cpp +++ b/samplecode/SampleXfermodes.cpp @@ -23,6 +23,12 @@ #include "SkColorPriv.h" #include "SkImageDecoder.h" +static void setNamedTypeface(SkPaint* paint, const char name[]) { + SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal); + paint->setTypeface(face); + SkSafeUnref(face); +} + #if 0 static int newscale(U8CPU a, U8CPU b, int shift) { unsigned prod = a * b + (1 << (shift - 1)); @@ -35,7 +41,7 @@ static void test_srcover565(SkCanvas* canvas) { bm1.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm1.allocPixels(NULL); bm2.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm2.allocPixels(NULL); bm3.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm3.allocPixels(NULL); - + int rgb = 0x18; int r = rgb >> 3; int g = rgb >> 2; @@ -44,7 +50,7 @@ static void test_srcover565(SkCanvas* canvas) { SkPMColor pm = SkPreMultiplyARGB(alpha, rgb, rgb, rgb); uint16_t newdst = SkSrcOver32To16(pm, dst); sk_memset16(bm1.getAddr16(0, alpha), newdst, bm1.width()); - + int ia = 255 - alpha; int iscale = SkAlpha255To256(ia); int dr = (SkGetPackedR32(pm) + (r * iscale >> 5)) >> 3; @@ -54,7 +60,7 @@ static void test_srcover565(SkCanvas* canvas) { int dr2 = (SkMulDiv255Round(alpha, rgb) + newscale(r, ia, 5)) >> 3; int dg2 = (SkMulDiv255Round(alpha, rgb) + newscale(g, ia, 6)) >> 2; - + sk_memset16(bm3.getAddr16(0, alpha), SkPackRGB16(dr2, dg2, dr2), bm3.width()); // if (mr != dr || mg != dg) @@ -62,13 +68,13 @@ static void test_srcover565(SkCanvas* canvas) { // SkDebugf("[%d] macro [%d %d] inline [%d %d] new [%d %d]\n", alpha, mr, mg, dr, dg, dr2, dg2); } } - + SkScalar dx = SkIntToScalar(width+4); - + canvas->drawBitmap(bm1, 0, 0, NULL); canvas->translate(dx, 0); canvas->drawBitmap(bm2, 0, 0, NULL); canvas->translate(dx, 0); canvas->drawBitmap(bm3, 0, 0, NULL); canvas->translate(dx, 0); - + SkRect rect = { 0, 0, SkIntToScalar(bm1.width()), SkIntToScalar(bm1.height()) }; SkPaint p; p.setARGB(0xFF, rgb, rgb, rgb); @@ -76,7 +82,7 @@ static void test_srcover565(SkCanvas* canvas) { } #endif -static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) { +static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) { src->setConfig(SkBitmap::kARGB_8888_Config, w, h); src->allocPixels(); src->eraseColor(0); @@ -88,10 +94,10 @@ static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) { SkScalar hh = SkIntToScalar(h); p.setAntiAlias(true); - p.setColor(0xFFFFCC44); + p.setColor(0xFFFFCC44); r.set(0, 0, ww*3/4, hh*3/4); c.drawOval(r, p); - + dst->setConfig(SkBitmap::kARGB_8888_Config, w, h); dst->allocPixels(); dst->eraseColor(0); @@ -112,26 +118,26 @@ class XfermodesView : public SkView { SkScalar x, SkScalar y) { SkPaint p; - canvas->drawBitmap(fSrcB, x, y, &p); + canvas->drawBitmap(fSrcB, x, y, &p); p.setAlpha(alpha); p.setXfermode(mode); canvas->drawBitmap(fDstB, x, y, &p); } - + public: const static int W = 64; const static int H = 64; XfermodesView() { const int W = 64; const int H = 64; - + fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4); fBG.setPixels(gBG); fBG.setIsOpaque(true); - + make_bitmaps(W, H, &fSrcB, &fDstB); } - + protected: // overrides from SkEventSink virtual bool onQuery(SkEvent* evt) { @@ -145,12 +151,12 @@ protected: void drawBG(SkCanvas* canvas) { canvas->drawColor(SK_ColorWHITE); } - + virtual void onDraw(SkCanvas* canvas) { canvas->translate(SkIntToScalar(10), SkIntToScalar(20)); this->drawBG(canvas); - + const struct { SkXfermode::Mode fMode; const char* fLabel; @@ -190,11 +196,14 @@ protected: SkMatrix m; m.setScale(SkIntToScalar(6), SkIntToScalar(6)); s->setLocalMatrix(m); - + SkPaint labelP; labelP.setAntiAlias(true); + labelP.setLCDRenderText(true); labelP.setTextAlign(SkPaint::kCenter_Align); - + setNamedTypeface(&labelP, "Menlo Regular"); +// labelP.setTextSize(SkIntToScalar(11)); + const int W = 5; SkScalar x0 = 0; @@ -210,7 +219,7 @@ protected: p.setStyle(SkPaint::kFill_Style); p.setShader(s); canvas->drawRect(r, p); - + canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag); // canvas->save(); draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop); diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp index 09cbe13710..d923315c16 100644 --- a/src/core/SkBlitter_ARGB32.cpp +++ b/src/core/SkBlitter_ARGB32.cpp @@ -38,6 +38,74 @@ extern uint32_t BlendLCDPixelWithBlack(const uint32_t alphaPixel, const uint32_t using namespace skia_blitter_support; #endif +/////////////////////////////////////////////////////////////////////////////// + +static int upscale31To256(int value) { + SkASSERT((unsigned)value <= 31); + // 0..31 -> 0..255 + value = (value << 3) | (value >> 2); + // 0..255 -> 0..256 + value += (value >> 7); + SkASSERT((unsigned)value <= 256); + return value; +} + +static void blit_lcd16_opaque(SkPMColor dst[], const uint16_t src[], + SkPMColor color, int width) { + int srcR = SkGetPackedR32(color); + int srcG = SkGetPackedG32(color); + int srcB = SkGetPackedB32(color); + + for (int i = 0; i < width; i++) { + uint16_t mask = src[i]; + if (0 == mask) { + continue; + } + + /* We want all of these in 5bits, hence the shifts in case one of them + * (green) is 6bits. + */ + int maskR = SkGetPackedR16(mask) >> (SK_R16_BITS - 5); + int maskG = SkGetPackedG16(mask) >> (SK_G16_BITS - 5); + int maskB = SkGetPackedB16(mask) >> (SK_B16_BITS - 5); + + // Now upscale them to 0..256, so we can use SkAlphaBlend + maskR = upscale31To256(maskR); + maskG = upscale31To256(maskG); + maskB = upscale31To256(maskB); + + int maskA = SkMax32(SkMax32(maskR, maskG), maskB); + + SkPMColor d = dst[i]; + int dstA = SkGetPackedA32(d); + int dstR = SkGetPackedR32(d); + int dstG = SkGetPackedG32(d); + int dstB = SkGetPackedB32(d); + + dst[i] = SkPackARGB32(SkAlphaBlend(0xFF, dstA, maskA), + SkAlphaBlend(srcR, dstR, maskR), + SkAlphaBlend(srcG, dstG, maskG), + SkAlphaBlend(srcB, dstB, maskB)); + } +} + +static void blitmask_lcd16(const SkBitmap& device, const SkMask& mask, + const SkIRect& clip, SkPMColor srcColor) { + int x = clip.fLeft; + int y = clip.fTop; + int width = clip.width(); + int height = clip.height(); + + SkPMColor* dstRow = device.getAddr32(x, y); + const uint16_t* srcRow = mask.getAddrLCD16(x, y); + + do { + blit_lcd16_opaque(dstRow, srcRow, srcColor, width); + dstRow = (SkPMColor*)((char*)dstRow + device.rowBytes()); + srcRow = (const uint16_t*)((const char*)srcRow + mask.fRowBytes); + } while (--height != 0); +} + ////////////////////////////////////////////////////////////////////////////////////// static void SkARGB32_Blit32(const SkBitmap& device, const SkMask& mask, @@ -189,6 +257,9 @@ void SkARGB32_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { } else if (SkMask::kARGB32_Format == mask.fFormat) { SkARGB32_Blit32(fDevice, mask, clip, fPMColor); return; + } else if (SkMask::kLCD16_Format == mask.fFormat) { + blitmask_lcd16(fDevice, mask, clip, fPMColor); + return; } int x = clip.fLeft; @@ -210,6 +281,9 @@ void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask, } else if (SkMask::kARGB32_Format == mask.fFormat) { SkARGB32_Blit32(fDevice, mask, clip, fPMColor); return; + } else if (SkMask::kLCD16_Format == mask.fFormat) { + blitmask_lcd16(fDevice, mask, clip, fPMColor); + return; } int x = clip.fLeft; @@ -319,6 +393,8 @@ void SkARGB32_Black_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { SkARGB32_BlitBW(fDevice, mask, clip, black); } else if (SkMask::kARGB32_Format == mask.fFormat) { SkARGB32_Blit32(fDevice, mask, clip, fPMColor); + } else if (SkMask::kLCD16_Format == mask.fFormat) { + blitmask_lcd16(fDevice, mask, clip, fPMColor); } else { #if defined(SK_SUPPORT_LCDTEXT) const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format; diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 0be0d8fbf1..3543f7497c 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -1104,6 +1104,22 @@ static void add_flattenable(SkDescriptor* desc, uint32_t tag, buffer->flatten(desc->addEntry(tag, buffer->size(), NULL)); } +static bool canSupportLCD16(const SkPaint& paint) { +#if 0 + return !paint.getShader() && + !paint.getXfermode() && // unless its srcover + !paint.getMaskFilter() && + !paint.getRasterizer() && + !paint.getColorFilter() && + !paint.getPathEffect() && + !paint.isFakeBoldText() && + paint.getStyle() == SkPaint::kFill_Style; +#else + // disable for now, while we test more + return false; +#endif +} + static SkMask::Format computeMaskFormat(const SkPaint& paint) { uint32_t flags = paint.getFlags(); @@ -1111,6 +1127,12 @@ static SkMask::Format computeMaskFormat(const SkPaint& paint) { if (!(flags & SkPaint::kAntiAlias_Flag)) return SkMask::kBW_Format; + if (flags & SkPaint::kLCDRenderText_Flag) { + if (canSupportLCD16(paint)) { + return SkMask::kLCD16_Format; + } + } + #if defined(SK_SUPPORT_LCDTEXT) if (flags & SkPaint::kLCDRenderText_Flag) { return SkFontHost::GetSubpixelOrientation() == SkFontHost::kHorizontal_LCDOrientation ? diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index bbe01585ec..ff8e1689d4 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -497,6 +497,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { if (NULL == fMaskFilter && fRec.fMaskFormat != SkMask::kBW_Format && + fRec.fMaskFormat != SkMask::kLCD16_Format && (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0) { const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable; diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp index c1540278f8..ae39361985 100644 --- a/src/ports/SkFontHost_mac_coretext.cpp +++ b/src/ports/SkFontHost_mac_coretext.cpp @@ -1,16 +1,16 @@ /* ** Copyright 2006, The Android Open Source Project ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at ** - ** http://www.apache.org/licenses/LICENSE-2.0 + ** http://www.apache.org/licenses/LICENSE-2.0 ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and ** limitations under the License. */ #include @@ -21,7 +21,7 @@ #include "SkString.h" #include "SkPaint.h" #include "SkFloatingPoint.h" - +#include "SkUtils.h" @@ -88,13 +88,13 @@ public: // Is a font ID valid? bool IsValid(SkFontID fontID); - - + + // Get a font CTFontRef GetFont(SkFontID fontID); SkNativeFontInfo GetFontInfo(const SkString &theName, SkTypeface::Style theStyle); - - + + // Create a font SkNativeFontInfo CreateFont(const SkString &theName, SkTypeface::Style theStyle); @@ -132,7 +132,7 @@ SkNativeFontCache::SkNativeFontCache(void) fontInfo.style = SkTypeface::kNormal; fontInfo.fontID = kSkInvalidFontID; fontInfo.fontRef = NULL; - + mFonts.push_back(fontInfo); } @@ -190,7 +190,7 @@ SkNativeFontInfo SkNativeFontCache::GetFontInfo(const SkString &theName, SkTypef if (theIter->name == theName && theIter->style == theStyle) return(*theIter); } - + return(fontInfo); } @@ -328,7 +328,8 @@ private: private: - CGColorSpaceRef mColorSpace; + CGColorSpaceRef mColorSpaceGray; + CGColorSpaceRef mColorSpaceRGB; CGAffineTransform mTransform; CTFontRef mFont; @@ -352,7 +353,11 @@ SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc) // Initialise ourselves - mColorSpace = CGColorSpaceCreateDeviceGray(); +// mColorSpaceRGB = CGColorSpaceCreateDeviceRGB(); +// mColorSpaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + mColorSpaceRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear); + mColorSpaceGray = CGColorSpaceCreateDeviceGray(); + const float inv = 1.0f / FONT_CANONICAL_POINTSIZE; mTransform = CGAffineTransformMake(SkScalarToFloat(skMatrix[SkMatrix::kMScaleX]) * inv, -SkScalarToFloat(skMatrix[SkMatrix::kMSkewY]) * inv, @@ -369,7 +374,8 @@ SkScalerContext_Mac::~SkScalerContext_Mac(void) { // Clean up - CFSafeRelease(mColorSpace); + CFSafeRelease(mColorSpaceGray); + CFSafeRelease(mColorSpaceRGB); CFSafeRelease(mFont); } @@ -437,8 +443,27 @@ void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) glyph->fLeft = sk_float_round2int(CGRectGetMinX(theBounds)); } -void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) -{ CGContextRef cgContext; +#include "SkColorPriv.h" + +static inline uint16_t rgb_to_lcd16(uint32_t rgb) { + int r = (rgb >> 16) & 0xFF; + int g = (rgb >> 8) & 0xFF; + int b = (rgb >> 0) & 0xFF; + + // invert, since we draw black-on-white, but we want the original + // src mask values. + r = 255 - r; + g = 255 - g; + b = 255 - b; + + return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b)); +} + +#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) +#define BITMAP_INFO_GRAY (kCGImageAlphaNone) + +void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) { + CGContextRef cgContext; CGGlyph cgGlyph; CGFontRef cgFont; @@ -447,17 +472,61 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount); cgFont = CTFontCopyGraphicsFont(mFont, NULL); - cgContext = CGBitmapContextCreate( glyph.fImage, glyph.fWidth, glyph.fHeight, 8, - glyph.rowBytes(), mColorSpace, kCGImageAlphaNone); + + SkAutoSMalloc<1024> storage; + + CGColorSpaceRef colorspace = mColorSpaceGray; + uint32_t info = BITMAP_INFO_GRAY; + void* image = glyph.fImage; + size_t rowBytes = glyph.rowBytes(); + float grayColor = 1; // white + + /* For LCD16, we first create a temp offscreen cg-context in 32bit, + * erase to white, and then draw a black glyph into it. Then we can + * extract the r,g,b values, invert-them, and now we have the original + * src mask components, which we pack into our 16bit mask. + */ + if (SkMask::kLCD16_Format == glyph.fMaskFormat) { + colorspace = mColorSpaceRGB; + info = BITMAP_INFO_RGB; + // need tmp storage for 32bit RGB offscreen + rowBytes = glyph.fWidth << 2; + size_t size = glyph.fHeight * rowBytes; + image = storage.realloc(size); + // we draw black-on-white (and invert in rgb_to_lcd16) + sk_memset32((uint32_t*)image, 0xFFFFFFFF, size >> 2); + grayColor = 0; // black + } + + cgContext = CGBitmapContextCreate(image, glyph.fWidth, glyph.fHeight, 8, + rowBytes, colorspace, info); // Draw the glyph if (cgFont != NULL && cgContext != NULL) { - CGContextSetGrayFillColor( cgContext, 1.0, 1.0); + CGContextSetAllowsFontSubpixelQuantization(cgContext, true); + CGContextSetShouldSubpixelQuantizeFonts(cgContext, true); + + CGContextSetGrayFillColor( cgContext, grayColor, 1.0); CGContextSetTextDrawingMode(cgContext, kCGTextFill); CGContextSetFont( cgContext, cgFont); CGContextSetFontSize( cgContext, FONT_CANONICAL_POINTSIZE); CGContextSetTextMatrix( cgContext, mTransform); CGContextShowGlyphsAtPoint( cgContext, -glyph.fLeft, glyph.fTop + glyph.fHeight, &cgGlyph, 1); + + if (SkMask::kLCD16_Format == glyph.fMaskFormat) { + // downsample from rgba to rgb565 + int width = glyph.fWidth; + const uint32_t* src = (const uint32_t*)image; + uint16_t* dst = (uint16_t*)glyph.fImage; + size_t dstRB = glyph.rowBytes(); + for (int y = 0; y < glyph.fHeight; y++) { + for (int i = 0; i < width; i++) { + dst[i] = rgb_to_lcd16(src[i]); + } + src = (const uint32_t*)((const char*)src + rowBytes); + dst = (uint16_t*)((char*)dst + dstRB); + } + } } // Clean up @@ -539,7 +608,7 @@ void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element case kCGPathElementCloseSubpath: skPath->close(); break; - + default: SkASSERT("Unknown path element!"); break; @@ -783,7 +852,7 @@ size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) theSize = CFDataGetLength(cfData); CFSafeRelease(cfData); } - + return(theSize); }