1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2011 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include "NinePatchPeeker.h"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <SkScalar.h>
20*d57664e9SAndroid Build Coastguard Worker #include <cutils/compiler.h>
21*d57664e9SAndroid Build Coastguard Worker
22*d57664e9SAndroid Build Coastguard Worker using namespace android;
23*d57664e9SAndroid Build Coastguard Worker
readChunk(const char tag[],const void * data,size_t length)24*d57664e9SAndroid Build Coastguard Worker bool NinePatchPeeker::readChunk(const char tag[], const void* data, size_t length) {
25*d57664e9SAndroid Build Coastguard Worker if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) {
26*d57664e9SAndroid Build Coastguard Worker Res_png_9patch* patch = (Res_png_9patch*) data;
27*d57664e9SAndroid Build Coastguard Worker size_t patchSize = patch->serializedSize();
28*d57664e9SAndroid Build Coastguard Worker if (length != patchSize) {
29*d57664e9SAndroid Build Coastguard Worker return false;
30*d57664e9SAndroid Build Coastguard Worker }
31*d57664e9SAndroid Build Coastguard Worker // You have to copy the data because it is owned by the png reader
32*d57664e9SAndroid Build Coastguard Worker Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
33*d57664e9SAndroid Build Coastguard Worker memcpy(patchNew, patch, patchSize);
34*d57664e9SAndroid Build Coastguard Worker Res_png_9patch::deserialize(patchNew);
35*d57664e9SAndroid Build Coastguard Worker patchNew->fileToDevice();
36*d57664e9SAndroid Build Coastguard Worker free(mPatch);
37*d57664e9SAndroid Build Coastguard Worker mPatch = patchNew;
38*d57664e9SAndroid Build Coastguard Worker mPatchSize = patchSize;
39*d57664e9SAndroid Build Coastguard Worker } else if (!strcmp("npLb", tag) && length == sizeof(int32_t) * 4) {
40*d57664e9SAndroid Build Coastguard Worker mHasInsets = true;
41*d57664e9SAndroid Build Coastguard Worker memcpy(&mOpticalInsets, data, sizeof(int32_t) * 4);
42*d57664e9SAndroid Build Coastguard Worker } else if (!strcmp("npOl", tag) && length == 24) { // 4 int32_ts, 1 float, 1 int32_t sized byte
43*d57664e9SAndroid Build Coastguard Worker mHasInsets = true;
44*d57664e9SAndroid Build Coastguard Worker memcpy(&mOutlineInsets, data, sizeof(int32_t) * 4);
45*d57664e9SAndroid Build Coastguard Worker mOutlineRadius = ((const float*)data)[4];
46*d57664e9SAndroid Build Coastguard Worker mOutlineAlpha = ((const int32_t*)data)[5] & 0xff;
47*d57664e9SAndroid Build Coastguard Worker }
48*d57664e9SAndroid Build Coastguard Worker return true; // keep on decoding
49*d57664e9SAndroid Build Coastguard Worker }
50*d57664e9SAndroid Build Coastguard Worker
scaleDivRange(int32_t * divs,int count,float scale,int maxValue)51*d57664e9SAndroid Build Coastguard Worker static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) {
52*d57664e9SAndroid Build Coastguard Worker for (int i = 0; i < count; i++) {
53*d57664e9SAndroid Build Coastguard Worker divs[i] = int32_t(divs[i] * scale + 0.5f);
54*d57664e9SAndroid Build Coastguard Worker if (i > 0 && divs[i] == divs[i - 1]) {
55*d57664e9SAndroid Build Coastguard Worker divs[i]++; // avoid collisions
56*d57664e9SAndroid Build Coastguard Worker }
57*d57664e9SAndroid Build Coastguard Worker }
58*d57664e9SAndroid Build Coastguard Worker
59*d57664e9SAndroid Build Coastguard Worker if (CC_UNLIKELY(divs[count - 1] > maxValue)) {
60*d57664e9SAndroid Build Coastguard Worker // if the collision avoidance above put some divs outside the bounds of the bitmap,
61*d57664e9SAndroid Build Coastguard Worker // slide outer stretchable divs inward to stay within bounds
62*d57664e9SAndroid Build Coastguard Worker int highestAvailable = maxValue;
63*d57664e9SAndroid Build Coastguard Worker for (int i = count - 1; i >= 0; i--) {
64*d57664e9SAndroid Build Coastguard Worker divs[i] = highestAvailable;
65*d57664e9SAndroid Build Coastguard Worker if (i > 0 && divs[i] <= divs[i-1]) {
66*d57664e9SAndroid Build Coastguard Worker // keep shifting
67*d57664e9SAndroid Build Coastguard Worker highestAvailable = divs[i] - 1;
68*d57664e9SAndroid Build Coastguard Worker } else {
69*d57664e9SAndroid Build Coastguard Worker break;
70*d57664e9SAndroid Build Coastguard Worker }
71*d57664e9SAndroid Build Coastguard Worker }
72*d57664e9SAndroid Build Coastguard Worker }
73*d57664e9SAndroid Build Coastguard Worker }
74*d57664e9SAndroid Build Coastguard Worker
scale(float scaleX,float scaleY,int scaledWidth,int scaledHeight)75*d57664e9SAndroid Build Coastguard Worker void NinePatchPeeker::scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight) {
76*d57664e9SAndroid Build Coastguard Worker if (!mPatch) {
77*d57664e9SAndroid Build Coastguard Worker return;
78*d57664e9SAndroid Build Coastguard Worker }
79*d57664e9SAndroid Build Coastguard Worker
80*d57664e9SAndroid Build Coastguard Worker // The max value for the divRange is one pixel less than the actual max to ensure that the size
81*d57664e9SAndroid Build Coastguard Worker // of the last div is not zero. A div of size 0 is considered invalid input and will not render.
82*d57664e9SAndroid Build Coastguard Worker if (!SkScalarNearlyEqual(scaleX, 1.0f)) {
83*d57664e9SAndroid Build Coastguard Worker mPatch->paddingLeft = int(mPatch->paddingLeft * scaleX + 0.5f);
84*d57664e9SAndroid Build Coastguard Worker mPatch->paddingRight = int(mPatch->paddingRight * scaleX + 0.5f);
85*d57664e9SAndroid Build Coastguard Worker scaleDivRange(mPatch->getXDivs(), mPatch->numXDivs, scaleX, scaledWidth - 1);
86*d57664e9SAndroid Build Coastguard Worker }
87*d57664e9SAndroid Build Coastguard Worker
88*d57664e9SAndroid Build Coastguard Worker if (!SkScalarNearlyEqual(scaleY, 1.0f)) {
89*d57664e9SAndroid Build Coastguard Worker mPatch->paddingTop = int(mPatch->paddingTop * scaleY + 0.5f);
90*d57664e9SAndroid Build Coastguard Worker mPatch->paddingBottom = int(mPatch->paddingBottom * scaleY + 0.5f);
91*d57664e9SAndroid Build Coastguard Worker scaleDivRange(mPatch->getYDivs(), mPatch->numYDivs, scaleY, scaledHeight - 1);
92*d57664e9SAndroid Build Coastguard Worker }
93*d57664e9SAndroid Build Coastguard Worker }
94