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