Compare commits

..

1 Commits

Author SHA1 Message Date
田双明 dd85bc466c
Pre Merge pull request !389 from 田双明/master 2024-09-27 06:34:28 +00:00
7 changed files with 98 additions and 139 deletions

View File

@ -12,7 +12,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { DownsampleStrategy } from '@ohos/imageknife';
import { IEngineKey, ImageKnifeOption, PixelMapTransformation,SparkMD5 ,ImageKnifeRequestSource} from '@ohos/libraryimageknife'; import { IEngineKey, ImageKnifeOption, PixelMapTransformation,SparkMD5 ,ImageKnifeRequestSource} from '@ohos/libraryimageknife';
//全局自定义key demo //全局自定义key demo
@ -36,9 +35,6 @@ export class CustomEngineKeyImpl implements IEngineKey {
if (imageKnifeOption.transformation) { if (imageKnifeOption.transformation) {
key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";" key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";"
} }
if ((imageKnifeOption.downsampleOf !== DownsampleStrategy.NONE && imageKnifeOption.downsampleOf !== undefined)) {
key += "downsampleOf" + imageKnifeOption.downsampleOf +"width="+width+"height="+ height
}
} }
return key return key
} }

View File

@ -132,8 +132,8 @@ struct DownSamplePage {
decodingOptions = { decodingOptions = {
editable: true, editable: true,
desiredSize: { desiredSize: {
width: reqSize.width, width: reqSize.targetWidth,
height: reqSize.height height: reqSize.targetHeight
} }
} }
// 创建pixelMap // 创建pixelMap

View File

@ -85,7 +85,7 @@ export class ImageKnifeLoader {
decodingOptions = ImageKnifeLoader.downsamplerReqSize(typeValue, request, size, ImageKnifeRequestSource.SRC) decodingOptions = ImageKnifeLoader.downsamplerReqSize(typeValue, request, size, ImageKnifeRequestSource.SRC)
} }
} catch (err) { } catch (err) {
return ImageKnifeLoader.makeEmptyResult(err) return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
} }
await imageSource.createPixelMap(decodingOptions) await imageSource.createPixelMap(decodingOptions)
@ -132,7 +132,7 @@ export class ImageKnifeLoader {
opts = ImageKnifeLoader.downsamplerReqSize(typeValue, request, size) opts = ImageKnifeLoader.downsamplerReqSize(typeValue, request, size)
} }
} catch (err) { } catch (err) {
return ImageKnifeLoader.makeEmptyResult(err) return ImageKnifeLoader.makeEmptyResult("image.createImageSource failed")
} }
await imageSource.createPixelMap(opts) await imageSource.createPixelMap(opts)
.then((pixelmap: PixelMap) => { .then((pixelmap: PixelMap) => {
@ -397,8 +397,8 @@ export class ImageKnifeLoader {
return ({ return ({
editable: true, editable: true,
desiredSize: ({ desiredSize: ({
height: vp2px(reqSize.height), height: vp2px(reqSize.targetHeight),
width: vp2px(reqSize.width) width: vp2px(reqSize.targetWidth)
} as Size) } as Size)
} as image.DecodingOptions) } as image.DecodingOptions)
@ -406,8 +406,8 @@ export class ImageKnifeLoader {
return ({ return ({
editable: request.requestSource === SRC && request.transformation !== undefined ? true : false, editable: request.requestSource === SRC && request.transformation !== undefined ? true : false,
desiredSize: ({ desiredSize: ({
width: reqSize.width, width: reqSize.targetWidth,
height: reqSize.height height: reqSize.targetHeight
} as Size) } as Size)
} as image.DecodingOptions) } as image.DecodingOptions)
} }

View File

@ -20,7 +20,7 @@ export interface BaseDownsampling {
getName(): string getName(): string
getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number,downsampType?:DownsampleStrategy): number getScaleFactor(sourceWidth: number, sourceHeight: number, requestWidth: number, requestHeight: number,downsampType?:DownsampleStrategy): number
//
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestWidth: number, getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestWidth: number,
requestHeight: number,downsampType?:DownsampleStrategy): SampleSizeRounding requestHeight: number,downsampType?:DownsampleStrategy): SampleSizeRounding
} }

View File

@ -13,36 +13,30 @@
* limitations under the License. * limitations under the License.
*/ */
import { BaseDownsampling } from './BaseDownsampling'; import { BaseDownsampling } from './BaseDownsampling';
import { getScale, highestOneBit, round, SampleSizeRounding } from './DownsampleUtils'; import { highestOneBit, SampleSizeRounding } from './DownsampleUtils';
export class FitCenter implements BaseDownsampling { export class FitCenter implements BaseDownsampling {
getName() { getName() {
return "FitCenter" return "FitCenter"
} }
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number, getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy |undefined
downsampType: DownsampleStrategy ): number {
): number { if (downsampType===DownsampleStrategy.FIT_CENTER_MEMORY) {
//重新计算宽高比; const widthPercentage = requestedWidth / sourceWidth
let outSize: Size = { const heightPercentage = requestedHeight / sourceHeight
width: round(getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) * sourceWidth), return Math.min(widthPercentage, heightPercentage)
height:round(getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) * sourceHeight) } else {
const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
const a = maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor)
return a;
} }
let scaleFactor = this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)===
SampleSizeRounding.QUALITY? Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height))) :
Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height)))//将整型的缩放因子转换为2的次幂采样大小
if (this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)===
SampleSizeRounding.MEMORY && (scaleFactor < (1 / getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)))) {
scaleFactor = scaleFactor << 1;
}
return scaleFactor
} }
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number, //实现 getSampleSizeRounding 方法
downsampType: DownsampleStrategy getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy|undefined
): number { ): number {
if (downsampType === DownsampleStrategy.FIT_CENTER_QUALITY) { if (downsampType===DownsampleStrategy.FIT_CENTER_QUALITY) {
return SampleSizeRounding.QUALITY; return SampleSizeRounding.QUALITY;
} else { } else {
return SampleSizeRounding.MEMORY; return SampleSizeRounding.MEMORY;
@ -58,19 +52,15 @@ export class None implements BaseDownsampling {
public getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, public getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number,
requestedHeight: number): number { requestedHeight: number): number {
let outSize: Size = { //不进行任何下采样,缩放因子为 1
width: round(sourceWidth), return 1;
height:round(sourceHeight)
}
let scaleFactor =
Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height)))
return scaleFactor
} }
//实现 getSampleSizeType 方法 //实现 getSampleSizeRounding 方法
public getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number, public getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number,
requestedHeight: number): SampleSizeRounding { requestedHeight: number): SampleSizeRounding {
return SampleSizeRounding.QUALITY return SampleSizeRounding.QUALITY
} }
} }
@ -87,16 +77,10 @@ export class CenterOutside implements BaseDownsampling {
const widthPercentage = requestedWidth / sourceWidth; const widthPercentage = requestedWidth / sourceWidth;
const heightPercentage = requestedHeight / sourceHeight; const heightPercentage = requestedHeight / sourceHeight;
//返回宽度和高度比例中最大的值 //返回宽度和高度比例中最大的值
let outSize: Size = { return Math.max(widthPercentage, heightPercentage);
width: round(Math.max(widthPercentage, heightPercentage) * sourceWidth),
height:round(Math.max(widthPercentage, heightPercentage) * sourceHeight)
}
let scaleFactor = Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height)))
return scaleFactor
} }
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number, getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number,
requestedHeight: number): SampleSizeRounding { requestedHeight: number): SampleSizeRounding {
return SampleSizeRounding.QUALITY; return SampleSizeRounding.QUALITY;
} }
@ -109,7 +93,7 @@ export class AtMost implements BaseDownsampling {
return "AtMost" return "AtMost"
} }
//实现 getScaleFactor 方法
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number { getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number): number {
const maxIntegerFactor = Math.ceil(Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth)); const maxIntegerFactor = Math.ceil(Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth));
let lesserOrEqualSampleSize = Math.max(1, highestOneBit(maxIntegerFactor)); let lesserOrEqualSampleSize = Math.max(1, highestOneBit(maxIntegerFactor));
@ -119,19 +103,11 @@ export class AtMost implements BaseDownsampling {
} }
greaterOrEqualSampleSize = lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0) greaterOrEqualSampleSize = lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0)
//返回缩放因子 //返回缩放因子
let outSize: Size = { return 1 / greaterOrEqualSampleSize
width: round((1 / greaterOrEqualSampleSize) * sourceWidth),
height:round((1 / greaterOrEqualSampleSize) * sourceHeight)
}
let scaleFactor = Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height)))
if ((scaleFactor < greaterOrEqualSampleSize)) {
scaleFactor = scaleFactor << 1;
}
return scaleFactor
} }
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number, getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number,
requestHeight: number): SampleSizeRounding { requestHeight: number): SampleSizeRounding {
//根据 AtMost 的逻辑,总是返回 MEMORY //根据 AtMost 的逻辑,总是返回 MEMORY
return SampleSizeRounding.MEMORY return SampleSizeRounding.MEMORY
@ -144,42 +120,43 @@ export class CenterInside implements BaseDownsampling {
getName() { getName() {
return "CenterInside" return "CenterInside"
} }
getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
downsampType: DownsampleStrategy
): number {
let outSize: Size = { getScaleFactor(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy|undefined
width: round(Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) * sourceWidth), ): number {
height:round(Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) * sourceHeight) //获取 FIT_CENTER 的缩放因子
} const fitCenterScaleFactor: ESObject =
//将整型的缩放因子转换为2的次幂采样大小 this.getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType);
let scaleFactor = this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, //返回不超过 1 的缩放因子,即尽量缩小图像以适应目标尺寸,但不会放大
downsampType) == SampleSizeRounding.QUALITY ? return Math.min(1, fitCenterScaleFactor);
Math.max(1, highestOneBit(Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height))) :
Math.max(1, highestOneBit(Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height)))
if (this.getSampleSizeType(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)
== SampleSizeRounding.MEMORY && (scaleFactor < (1 / Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType))))) {
scaleFactor = scaleFactor << 1;
}
return scaleFactor
} }
getSampleSizeType(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number, getScale(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy|undefined
downsampType: DownsampleStrategy ): number {
): SampleSizeRounding { if (downsampType===DownsampleStrategy.FIT_CENTER_MEMORY) {
const widthPercentage = requestedWidth / sourceWidth
const heightPercentage = requestedHeight / sourceHeight
return Math.min(widthPercentage, heightPercentage)
} else {
const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
const a = maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor)
return a;
}
}
getSampleSizeRounding(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy|undefined
): SampleSizeRounding {
//如果缩放因子为 1表示没有缩放优先选择质量 //如果缩放因子为 1表示没有缩放优先选择质量
if (Math.min(1, getScale(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType)) === 1) { if (this.getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType) === 1) {
return SampleSizeRounding.QUALITY return SampleSizeRounding.QUALITY
} }
//否则,使用 FIL_CENTER 的 SampleSizeRounding 值 //否则,使用 FIL_CENTER 的 SampleSizeRounding 值
return this.getSampleSize(sourceWidth, sourceHeight, requestedWidth, requestedHeight, downsampType); return this.getSampleSize(sourceWidth, sourceHeight, requestedWidth, requestedHeight,downsampType);
} }
getSampleSize(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number, getSampleSize(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,downsampType:DownsampleStrategy|undefined
downsampType: DownsampleStrategy ): SampleSizeRounding {
): SampleSizeRounding { if (downsampType===DownsampleStrategy.CENTER_INSIDE_MEMORY) {
if (downsampType === DownsampleStrategy.CENTER_INSIDE_MEMORY) {
return SampleSizeRounding.MEMORY; return SampleSizeRounding.MEMORY;
} else { } else {
return SampleSizeRounding.QUALITY; return SampleSizeRounding.QUALITY;
@ -191,11 +168,11 @@ export enum DownsampleStrategy {
//请求尺寸大于实际尺寸不进行放大 //请求尺寸大于实际尺寸不进行放大
AT_MOST, AT_MOST,
//两边自适应内存优先 //两边自适应内存优先
FIT_CENTER_MEMORY, FIT_CENTER_MEMORY ,
//两边自适应质量优先 //两边自适应质量优先
FIT_CENTER_QUALITY, FIT_CENTER_QUALITY,
//按照宽高比的最大比进行适配内存优先 //按照宽高比的最大比进行适配内存优先
CENTER_INSIDE_MEMORY, CENTER_INSIDE_MEMORY ,
//按照宽高比的最大比进行适配质量优先 //按照宽高比的最大比进行适配质量优先
CENTER_INSIDE_QUALITY, CENTER_INSIDE_QUALITY,
//宽高进行等比缩放宽高里面最小的比例先放进去,然后再根据原图的缩放比去适配 //宽高进行等比缩放宽高里面最小的比例先放进去,然后再根据原图的缩放比去适配

View File

@ -12,8 +12,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { DownsampleStrategy } from './DownsampleStartegy';
export enum SampleSizeRounding { export enum SampleSizeRounding {
/** /**
* Prefer to round the sample size up so that the image is downsampled to smaller than the * Prefer to round the sample size up so that the image is downsampled to smaller than the
@ -28,7 +26,6 @@ export enum SampleSizeRounding {
//(质量优先) //(质量优先)
QUALITY QUALITY
} }
export function highestOneBit(i: number): number { export function highestOneBit(i: number): number {
i |= (i >> 1); i |= (i >> 1);
i |= (i >> 2); i |= (i >> 2);
@ -36,22 +33,4 @@ export function highestOneBit(i: number): number {
i |= (i >> 8); i |= (i >> 8);
i |= (i >> 16); i |= (i >> 16);
return i - (i >>> 1); return i - (i >>> 1);
}
export function getScale(sourceWidth: number, sourceHeight: number, requestedWidth: number, requestedHeight: number,
downsampType: DownsampleStrategy
): number {
if (downsampType === DownsampleStrategy.FIT_CENTER_MEMORY) {
const widthPercentage = requestedWidth / sourceWidth
const heightPercentage = requestedHeight / sourceHeight
return Math.min(widthPercentage, heightPercentage)
} else {
const maxIntegerFactor = Math.max(sourceHeight / requestedHeight, sourceWidth / requestedWidth);
return maxIntegerFactor === 0 ? 1 : 1 / highestOneBit(maxIntegerFactor);
}
}
export function round(value: number): number {
return Math.floor(value + 0.5);
} }

View File

@ -20,7 +20,12 @@ import {
FitCenter, FitCenter,
None, None,
} from './DownsampleStartegy'; } from './DownsampleStartegy';
import { highestOneBit, round, SampleSizeRounding } from './DownsampleUtils'; import { highestOneBit, SampleSizeRounding } from './DownsampleUtils';
export interface calculateScaleType {
targetWidth: number,
targetHeight: number
}
export class Downsampler { export class Downsampler {
@ -31,45 +36,43 @@ export class Downsampler {
requestWidth: number, //请求宽高 requestWidth: number, //请求宽高
requestHeight: number, //请求宽高 requestHeight: number, //请求宽高
downsampType: DownsampleStrategy, downsampType: DownsampleStrategy,
): Size { ): calculateScaleType {
if (sourceHeight <= 0 || sourceWidth <= 0) { if (sourceHeight <= 0 || sourceWidth <= 0) {
throw new Error(`Invalid width and height, sourceHeight:${sourceHeight}+ sourceWidth:${sourceWidth}`) throw new Error(`Invalid width and height, sourceHeight:${sourceHeight}+ sourceWidth:${sourceWidth}`)
} }
let downsampler = this.getDownsampler(downsampType); let downsampler = this.getDownsampler(downsampType);
let scaleFactor: number = let exactScaleFactor: number =
downsampler.getScaleFactor(sourceWidth, sourceHeight, requestWidth, requestHeight, downsampType);//缩放比 downsampler.getScaleFactor(sourceWidth, sourceHeight, requestWidth, requestHeight, downsampType);
// let rounding: SampleSizeRounding = let rounding: SampleSizeRounding =
// downsampler.getSampleSizeType(sourceWidth, sourceHeight, requestWidth, requestHeight, downsampType); //采样类型 downsampler.getSampleSizeRounding(sourceWidth, sourceHeight, requestWidth, requestHeight, downsampType); //采样类型
//根据缩放比重新计算宽高 //原始宽高和缩放系数的乘积
// let outSize: Size = { let outSize:Size = {
// width: round(exactScaleFactor * sourceWidth), width: this.round(exactScaleFactor * sourceWidth),
// height:round(exactScaleFactor * sourceHeight) height: this.round(exactScaleFactor * sourceHeight)
// } }
// let scaleFactor = let scaleFactor =
// rounding == SampleSizeRounding.QUALITY ? Math.max(sourceWidth / exactScaleFactor.width, sourceHeight / exactScaleFactor.height) : rounding == SampleSizeRounding.QUALITY ? Math.max(sourceWidth / outSize.width, sourceHeight / outSize.height) :
// Math.min(sourceWidth / exactScaleFactor.width, sourceHeight / exactScaleFactor.height) //重新计算宽高比 Math.min(sourceWidth / outSize.width, sourceHeight / outSize.height) //将整型的缩放因子转换为2的次幂采样大小
scaleFactor = Math.max(1, highestOneBit(scaleFactor))
// scaleFactor = Math.max(1, highestOneBit(scaleFactor))//将整型的缩放因子转换为2的次幂采样大小 if (rounding == 0 && (scaleFactor < (1 / exactScaleFactor))) {
// scaleFactor = scaleFactor << 1;
// if (rounding == 0 && (scaleFactor < (1 / exactScaleFactor))) { }
// scaleFactor = scaleFactor << 1;
// }
//基于上一步得出的采样大小,根据不同的图片类型,计算采样后的图片尺寸 //基于上一步得出的采样大小,根据不同的图片类型,计算采样后的图片尺寸
if (typeValue === "png") { if (typeValue === "png") {
return { return {
width: Math.floor(sourceWidth / scaleFactor), targetWidth: Math.floor(sourceWidth / scaleFactor),
height: Math.floor(sourceHeight / scaleFactor) targetHeight: Math.floor(sourceHeight / scaleFactor)
} }
} else if (typeValue === "webp") { } else if (typeValue === "webp") {
return { return {
width: Math.round(sourceWidth / scaleFactor), targetWidth: Math.round(sourceWidth / scaleFactor),
height: Math.round(sourceHeight / scaleFactor) targetHeight: Math.round(sourceHeight / scaleFactor)
} }
} else { } else {
return { return {
width: sourceWidth / scaleFactor, targetWidth: sourceWidth / scaleFactor,
height: sourceHeight / scaleFactor targetHeight: sourceHeight / scaleFactor
} }
} }
@ -93,4 +96,8 @@ export class Downsampler {
throw new Error('Unsupported downsampling strategy'); throw new Error('Unsupported downsampling strategy');
} }
} }
round(value: number): number {
return Math.floor(value + 0.5);
}
} }