size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy, const DeferredTextureImageUsageParams params[], int paramCnt, void* buffer, SkColorSpace* dstColorSpace) const { // Extract relevant min/max values from the params array. int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel; SkFilterQuality highestFilterQuality = params[0].fQuality; bool useMipMaps = should_use_mip_maps(params[0]); for (int i = 1; i < paramCnt; ++i) { if (lowestPreScaleMipLevel > params[i].fPreScaleMipLevel) lowestPreScaleMipLevel = params[i].fPreScaleMipLevel; if (highestFilterQuality < params[i].fQuality) highestFilterQuality = params[i].fQuality; useMipMaps |= should_use_mip_maps(params[i]); } const bool fillMode = SkToBool(buffer); if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) { return 0; } // Calculate scaling parameters. bool isScaled = lowestPreScaleMipLevel != 0; SkISize scaledSize; if (isScaled) { // SkMipMap::ComputeLevelSize takes an index into an SkMipMap. SkMipMaps don't contain the // base level, so to get an SkMipMap index we must subtract one from the GL MipMap level. scaledSize = SkMipMap::ComputeLevelSize(this->width(), this->height(), lowestPreScaleMipLevel - 1); } else { scaledSize = SkISize::Make(this->width(), this->height()); } // We never want to scale at higher than SW medium quality, as SW medium matches GPU high. SkFilterQuality scaleFilterQuality = highestFilterQuality; if (scaleFilterQuality > kMedium_SkFilterQuality) { scaleFilterQuality = kMedium_SkFilterQuality; } const int maxTextureSize = proxy.fCaps->maxTextureSize(); if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) { return 0; } SkAutoPixmapStorage pixmap; SkImageInfo info; size_t pixelSize = 0; size_t ctSize = 0; int ctCount = 0; if (!isScaled && this->peekPixels(&pixmap)) { info = pixmap.info(); pixelSize = SkAlign8(pixmap.getSafeSize()); if (pixmap.ctable()) { ctCount = pixmap.ctable()->count(); ctSize = SkAlign8(pixmap.ctable()->count() * 4); } } else { // Here we're just using presence of data to know whether there is a codec behind the image. // In the future we will access the cacherator and get the exact data that we want to (e.g. // yuv planes) upload. sk_sp<SkData> data(this->refEncoded()); if (!data && !this->peekPixels(nullptr)) { return 0; } info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height()); pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr)); if (fillMode) { pixmap.alloc(info); if (isScaled) { if (!this->scalePixels(pixmap, scaleFilterQuality, SkImage::kDisallow_CachingHint)) { return 0; } } else { if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) { return 0; } } SkASSERT(!pixmap.ctable()); } } int mipMapLevelCount = 1; if (useMipMaps) { // SkMipMap only deals with the mipmap levels it generates, which does // not include the base level. // That means it generates and holds levels 1-x instead of 0-x. // So the total mipmap level count is 1 more than what // SkMipMap::ComputeLevelCount returns. mipMapLevelCount = SkMipMap::ComputeLevelCount(scaledSize.width(), scaledSize.height()) + 1; // We already initialized pixelSize to the size of the base level. // SkMipMap will generate the extra mipmap levels. Their sizes need to // be added to the total. // Index 0 here does not refer to the base mipmap level -- it is // SkMipMap's first generated mipmap level (level 1). for (int currentMipMapLevelIndex = mipMapLevelCount - 2; currentMipMapLevelIndex >= 0; currentMipMapLevelIndex--) { SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(), currentMipMapLevelIndex); SkImageInfo mipInfo = info.makeWH(mipSize.fWidth, mipSize.fHeight); pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr)); } } size_t size = 0; size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage)); size += dtiSize; size += (mipMapLevelCount - 1) * sizeof(MipMapLevelData); // We subtract 1 because DeferredTextureImage already includes the base // level in its size size_t pixelOffset = size; size += pixelSize; size_t ctOffset = size; size += ctSize; size_t colorSpaceOffset = 0; size_t colorSpaceSize = 0; if (info.colorSpace()) { colorSpaceOffset = size; colorSpaceSize = info.colorSpace()->writeToMemory(nullptr); size += colorSpaceSize; } if (!fillMode) { return size; } char* bufferAsCharPtr = reinterpret_cast<char*>(buffer); char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset; void* pixels = pixelsAsCharPtr; void* ct = nullptr; if (ctSize) { ct = bufferAsCharPtr + ctOffset; } memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))), pixmap.addr(), pixmap.getSafeSize()); if (ctSize) { memcpy(ct, pixmap.ctable()->readColors(), ctSize); } // If the context has sRGB support, and we're intending to render to a surface with an attached // color space, and the image has an sRGB-like color space attached, then use our gamma (sRGB) // aware mip-mapping. SkSourceGammaTreatment gammaTreatment = SkSourceGammaTreatment::kIgnore; if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) && info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) { gammaTreatment = SkSourceGammaTreatment::kRespect; } SkASSERT(info == pixmap.info()); size_t rowBytes = pixmap.rowBytes(); static_assert(std::is_standard_layout<DeferredTextureImage>::value, "offsetof, which we use below, requires the type have standard layout"); auto dtiBufferFiller = DTIBufferFiller{bufferAsCharPtr}; FILL_MEMBER(dtiBufferFiller, fGammaTreatment, &gammaTreatment); FILL_MEMBER(dtiBufferFiller, fContextUniqueID, &proxy.fContextUniqueID); int width = info.width(); FILL_MEMBER(dtiBufferFiller, fWidth, &width); int height = info.height(); FILL_MEMBER(dtiBufferFiller, fHeight, &height); SkColorType colorType = info.colorType(); FILL_MEMBER(dtiBufferFiller, fColorType, &colorType); SkAlphaType alphaType = info.alphaType(); FILL_MEMBER(dtiBufferFiller, fAlphaType, &alphaType); FILL_MEMBER(dtiBufferFiller, fColorTableCnt, &ctCount); FILL_MEMBER(dtiBufferFiller, fColorTableData, &ct); FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount, &mipMapLevelCount); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData), &pixels, sizeof(pixels)); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fRowBytes), &rowBytes, sizeof(rowBytes)); if (colorSpaceSize) { void* colorSpace = bufferAsCharPtr + colorSpaceOffset; FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace); FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize); info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset); } else { memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpace), 0, sizeof(DeferredTextureImage::fColorSpace)); memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpaceSize), 0, sizeof(DeferredTextureImage::fColorSpaceSize)); } // Fill in the mipmap levels if they exist char* mipLevelPtr = pixelsAsCharPtr + SkAlign8(pixmap.getSafeSize()); if (useMipMaps) { static_assert(std::is_standard_layout<MipMapLevelData>::value, "offsetof, which we use below, requires the type have a standard layout"); SkAutoTDelete<SkMipMap> mipmaps(SkMipMap::Build(pixmap, gammaTreatment, nullptr)); // SkMipMap holds only the mipmap levels it generates. // A programmer can use the data they provided to SkMipMap::Build as level 0. // So the SkMipMap provides levels 1-x but it stores them in its own // range 0-(x-1). for (int generatedMipLevelIndex = 0; generatedMipLevelIndex < mipMapLevelCount - 1; generatedMipLevelIndex++) { SkMipMap::Level mipLevel; mipmaps->getLevel(generatedMipLevelIndex, &mipLevel); // Make sure the mipmap data is after the start of the buffer SkASSERT(mipLevelPtr > bufferAsCharPtr); // Make sure the mipmap data starts before the end of the buffer SkASSERT(mipLevelPtr < bufferAsCharPtr + pixelOffset + pixelSize); // Make sure the mipmap data ends before the end of the buffer SkASSERT(mipLevelPtr + mipLevel.fPixmap.getSafeSize() <= bufferAsCharPtr + pixelOffset + pixelSize); // getSafeSize includes rowbyte padding except for the last row, // right? memcpy(mipLevelPtr, mipLevel.fPixmap.addr(), mipLevel.fPixmap.getSafeSize()); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) + sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) + offsetof(MipMapLevelData, fPixelData), &mipLevelPtr, sizeof(void*)); size_t rowBytes = mipLevel.fPixmap.rowBytes(); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) + sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) + offsetof(MipMapLevelData, fRowBytes), &rowBytes, sizeof(rowBytes)); mipLevelPtr += SkAlign8(mipLevel.fPixmap.getSafeSize()); } } return size; }
static int perspective_run(const FilterActivation *fa, const FilterFunctions *ff) { PerspectiveFilterData *mfd = (PerspectiveFilterData *)fa->filter_data; VDPixmap pxsrc = {0}; VDPixmap pxdst = {0}; pxsrc.data = (char *)fa->src.data + fa->src.pitch * (fa->src.h-1); pxsrc.pitch = -fa->src.pitch; pxsrc.format = nsVDPixmap::kPixFormat_XRGB8888; pxsrc.w = fa->src.w; pxsrc.h = fa->src.h; pxdst.data = (char *)fa->dst.data + fa->dst.pitch * (fa->dst.h-1); pxdst.pitch = -fa->dst.pitch; pxdst.format = nsVDPixmap::kPixFormat_XRGB8888; pxdst.w = fa->dst.w; pxdst.h = fa->dst.h; VDPixmapTextureMipmapChain mipmaps(pxsrc, false, mfd->filtermode ? 16 : 1); VDTriBltVertex trivx[8]={ { -1, -1, 0, 0, 0 }, { +1, -1, 0, (float)pxsrc.w, 0 }, { +1, +1, 0, (float)pxsrc.w, (float)pxsrc.h }, { -1, +1, 0, 0, (float)pxsrc.h }, }; static const int indices[6]={0,1,2,0,2,3}; vdfloat3x3 temp(MapQuadToUnitSquare(mfd->src)); if (!mfd->unproject) temp = ~temp; vdfloat4x4 mx; mx[0][0] = temp[0][0]; mx[0][1] = temp[0][1]; mx[0][2] = 0; mx[0][3] = temp[0][2]; mx[1][0] = temp[1][0]; mx[1][1] = temp[1][1]; mx[1][2] = 0; mx[1][3] = temp[1][2]; mx[2][0] = 0; mx[2][1] = 0; mx[2][3] = 0; mx[2][2] = 0; mx[3][0] = temp[2][0]; mx[3][1] = temp[2][1]; mx[3][2] = 0; mx[3][3] = temp[2][2]; // manually transform corners and form fill mesh for(int i=0; i<4; ++i) { vdfloat3 xfv((mx*vdfloat4c(trivx[i].x, trivx[i].y, trivx[i].z, 1.0f)).project()); trivx[i+4].x = xfv[0]; trivx[i+4].y = xfv[1]; trivx[i+4].z = xfv[2]; } static const int fillmesh[24]={ 0,1,5,5,4,0, 1,2,6,6,5,1, 2,3,7,7,6,2, 3,0,4,4,7,3 }; VDPixmapTriFill(pxdst, 0, trivx, 8, fillmesh, 24, NULL); // texture image VDPixmapTriBlt(pxdst, mipmaps.Mips(), mipmaps.Levels(), trivx, 4, indices, 6, (VDTriBltFilterMode)mfd->filtermode, true, mx.data()); return 0; }